MVP + RxJava + Retrofit + Dagger2 如今已经成了Android开发的主流框架,为了不落伍,我也开始了这个组合框架的学习,力求摆脱之前的那种简单粗暴的分包模式。于是也开始了 Dagger2 的学习之路。这几天断断续续的也看了很多文章,其中当然有很多优秀的文章,但是慢慢的觉得并没有哪一篇文章,是完全的能让人只需要看一篇就能完全让一个人从0开始入门的,Dagger2这个框架学习起来还是比较费劲的,往往是搜了很多文章,综合起来才慢慢的能把很多东西搞懂,这就使得很多人还没入门就多次放弃了(说实话,我也放弃了很多次)。在这里,我想力求做到从一个“白痴”的角度(我是不会承认是我的)进行讲解,以问题为导向来讲解,很多时候我们觉得写博客的人虽然写了很多,觉得很牛逼,但可恨的是,我们还是不懂。其实这并不是我们的错,只是作者忘记了当初自己是怎么过来的,各种问题是怎么一步步被提出来的,结果就是变成了教科书式的讲解。下面就让我们一切从头来过,这一次,决不放弃。
什么是依赖注入,为什么我们需要Dagger2
这里,我就不在粘贴那些很官方的描述了,直接大白话。“依赖注入”,从这个词来说,就是 “依赖” 和 “注入” 两个部分嘛。所谓“依赖”,和我们生活中理解的一样,比如说汽车和汽油的关系,汽车只有在有汽油存在的情况下才有意义,严重依赖汽油。而“注入”,很容易让我们想到注射器,将一种液体注入到另一种容器中,这里的“注入”也是一样的,只不过是讲对象绑定到另一个类中而已(简单点来讲就是通过引用进行赋值)。
那为什么需要依赖注入呢?其实就是为了简化无用操作,提高效率,并且对代码进行解耦,使得需求或者一个地方的代码变动,尽可能小的引起其他部分代码的修改。我们以箱子和箱子的水果这个场景为例,进行讲解。
首先创建一个Box类,在其中又依赖了两个类:Apple 和 Banana。
public class Box {
Apple mApple;
public Box(Apple apple){
mApple = apple;
}
}
箱子里装有苹果。下面看具体使用:
public class MainActivity extends AppCompatActivity {
Apple mApple;
Box mBox;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mApple = new Apple(); // 先创建 Apple 对象
mBox = new Box(mApple, mBanana); // 将创建好了的 Apple 对象传入 Box 对象的构造函数中
}
可以看到,我们要向得到一个Box对象,必须要先手动生成一个Apple对象和一个Banana对象,试想一下,如果在以后,我们修改了Apple类,在其中添加了对苹果的描述,改变了构造方法,比如变为:
public class Apple {
public Apple(String desctiption){
Log.i("qc","苹果的描述为: ");
}
}
那我们就需要再去修改 MainActivity.java 类,显然按,这种编程习惯是不好的。我们能不能通过其他方法,来降低这种影响呢?其实,在编程中我们很多时候是用到过的,只是我们没意识到,想想,在列表加载中,为适配器设置数据、为列表项加载头布局和尾布局的时候你是怎么设置的?Bingo,通过setXXX(...)方法,比如,在Adapter中设置一个public方法来设置数据集:
public void setData(List data){
...
}
代码编写的多了,就会更加的接近设计模式、编程思想的本质,毕竟设计模式源于实践并且指导实践的,我们可能再也不愿意去直接在创建Adapter的构造函数中直接传入数据集了。
如果觉得这点我说的不太清楚的话,可以参考这篇文章:使用Dagger2前你必须了解的一些设计原则
其实,Dagger2 的思想,跟这个就很像,都是为了解耦。使用 Dagger2 ,这些setXXX(...)的方法,完全就不需要我们自己去做了。
这里再补充一点,如果你熟悉MVP的话,就很容易能感受到 Dagger2 的魅力,MVP 模式中,View和 Presenter 互相持有对方的引用,而 Presenter 同时又持有 Model 的引用,这样,就造成了View和 Presenter 之间的耦合性比较大,使用 Dagger2 可以减弱这种耦合,直接将 Presenter 通过依赖注入的方式注入到 View 中(例如Activity),当然,如果你还不了解MVP的话,现在学习这篇文章,千万别再跑去看MVP,而丢了主次,完全不需要,那只是 Dagger2 的一个使用场景。
Dagger2学习需要的基础:注解
在学习Dagger2之前,你一定要了解的东西就是Java中的注解,可以说如果不懂注解的话,学习Dagger2 是非常痛苦的一件事,甚至完全不知道整个过程是怎么进行的。关于注解,并不难,一两篇文章就可以搞懂,不会花费你很多时间的,可以参考这篇文章:JAVA 注解的几大作用及使用方法详解
以前我们往往是在需要的地方手动的去通过 new 来生成对象,对象的生成和对象的使用是在同一个地方,而 Dagger2 将对象的是生成和其使用分开。说白了,Dagger2 的整个过程就两个:
创建并初始化对象(被依赖的对象,比如 Apple)
将初始化好的对象通过“中间人”放到需要它的那个对象中(目标类,或者成为容器,比如 Box)
那么就让我们直接通过具体的例子来进行讲解,让大家更直观的感受到整个过程。
Dagger2 具体使用
1.使用Dagger2 需要的相关配置
现在Dagger2抛弃了之前采用的apt自动生成代码的方式,而采用annotationProcessor,因而
如果你使用的Gradle插件版本在2.2及以上,只需要在应用对应的build.gradle文件中添加以下依赖即可:
dependencies {
...
...
// Dagger2相关依赖
compile 'com.google.dagger:dagger:2.11-rc2'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
}
如果用的是比较旧的版本,可以参考GitHub上项目帮助文档:https://bitbucket.org/hvisser...
2.如何生成对象
既然是依赖注入,我们首先需要做的就是把对象生产出来。那怎么生成一个对象呢,很容易想到在构造函数前加一个 new ,如:new Apple(),但是既然 Dagger2 能让它自动生成,我们当然不需要自己动手,只需要做某种标记,比如可以直接加上@Inject注解。
这里我们首先提出来,生成所需对象的方式有两种:
@Inject 注解提供方式:在所需类的构造函数中直接加上 @Inject 注解
@Module 注解提供方式:通过新建一个专门的提供这类来提供类实例,然后在类名上面添加 @Module 注解,在类中自己定义方法,手动通过 new 来进行创建,这种主要是针对第三方库中,我们无法直接在构造函数中添加 @Inject 注解的情况。
3.具体实施步骤
这里,我们先针对第一种情况进行讲解,也就是 @Inject 注解,先跑通整个流程
下面一步步讲解:
第一步,很简单:就是在Apple类的构造函数上添加 @Inject 注解标记
public class Apple {
@Inject // 注意此处的标记是加在构造函数上面的,这个是无参构造函数,有参构造函数的情况下之后会讲
public Apple(){
}
public String getDescription(){
return "这个苹果真好吃";
}
}
这样,我们就完成了提供者端的操作了,很简单吧,仅仅只是在构造函数上面添加了一个 @Inject 注解。
这一步,是告诉编译器及其他处理程序,我们需要通过此处来生成对象,从而供以后进行依赖注入。
第二步,在目标类中的对应成员变量上也添加相同的注解 @Inject (注意,在目标类中这个注解始终是 @Inject ,而不会因为之后使用 @Module 这种方式而改变)
public class MainActivity extends AppCompatActivity {
@Inject
Apple mApple; // 在这里,我们添加了@Inject 注解,告诉处理程序,我们需要将对象注入到此处,给此引用赋值
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在这一步,我们通过在目标类中属性上添加 @Inject 注解,来告诉编译器和其他代码自动生成程序,我们需要将之前生成的对象注入到这里(赋值给这里的引用)。
注意,属性的访问修饰符一定不能是private类型的,否则无法通过Dagger2进行依赖注入,因为我们其实是通过传入引用,然后通过引用来调用其中的属性,一旦声明为private,我们自然无法通过mainActivity.mApple
这种方式来进行赋值操作了。
至此,我们就完成了最基本的两步了,我们知道了从哪里生成对象,也知道了生成之后的对象需要在哪里使用,但是,如何将二者联系起来呢,也就是说,我如何通过某种方式,将生成的对象送达到需要的地方呢?这就需要一个“中间人”来起到桥梁的作用,让二者产生实质上的关联。这就是接下来要讲到的 @Component 注解了。
第三步,通过 @Component 进行牵线搭桥
我们最好建立一个包,名叫component,以方便管理。在该包下新建一个接口类,建议统一以Component作为后缀,比如下面的 MainComponent.java 。
@Component
public interface MainComponent {
void inject(MainActivity activity);
}
我们在该接口上面添加了 @Component 注解,同时定义了一个抽象方法:void inject(MainActivity activity);
注意,这个方法,返回值是void
类型的,后面的方法名可以随便起,括号内的参数是我们需要将对象注入的类,一定要是具体的类,而不能是其父类。其实,之后我们使用的时候,就是将对应的Activity
传进去,然后通过引用调用该对象的属性进行赋值,思想很简单。这里要强调一点,这里并不是一定要求是void
类型的,可以有具体的返回值,这种情况我们会在之后具体讨论,这里先通过这个void
简单类型来学习,避免复杂化。函数名也一般先命名为inject
,便于理解。
其实,写到这里,我们的整个过程基本上就完成了,下面只需要通过代码,进行实际的注入就好了。可能你会问,这就完成了?我们上面只是定义了一个接口,并没有具体的实现啊,怎么就能注入呢?接下来,我们就来见证奇迹的时刻把,我们把项目编译一下,或者直接点击AS上的绿色的锤子图标:
然后你打开下图的目录,就会发现会自动生成一些类。
这就是我们在build.gradley依赖中添加的注解处理器(AnnotationProcess)给我们自动生成的。之前是没有apt这整个目录的,这里面我们可以看到,它也按照我们分的包的结构来生成的,比如“component”包。这里面现在对我们来讲最重要的也是直接跟我们发生关系的就是DaggerMainComponent.java
这个类,我们进行具体的依赖注入其实就是使用的这个类,这个类是Dagger2框架自动为我们生成的,可以看到,它是以Dagger开头的。当我们创建了一个Component,比如MainComponent之后,系统会自动为我们生成一个以Dagger为前缀,后面跟上我们的Component名的一个具体实现类。先满足一下你的好奇心,我们点进去,看一下这个类。
public final class DaggerMainComponent implements MainComponent {
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerMainComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static MainComponent create() {
return new Builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(Apple_Factory.create());
}
// 看到了吧,这个就是我们在之前定义的接口,此处系统自动为我们进行了实现
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private Builder() {}
public MainComponent build() {
return new DaggerMainComponent(this);
}
}
}
可以看到,它实现了我们之前写的MainComponent接口,里面也对我们之前定义的抽象方法进行了实现。
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
到这里知道为啥我说名字可以随便命名了吧。
好啦,看到这里就够了,千万别陷进去,一会儿我们还会回来继续分析这个类的,千万别觉得我是在浅尝辄止啊。
还是再提醒一下,编写完之后,一定要编译一下项目,否则这些不会生成的,下面你也没法用Dagger***Component
来进行注入了,会提醒你找不到该类。
第四步,也是最后一步,使用Dagger***Component
进行依赖注入
我们只需要在我们的目标类中,调用注入代码就可以了,如下代码:
public class MainActivity extends AppCompatActivity {
@Inject
Apple mApple;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 将依赖注入到该Activity中
DaggerMainComponent
.builder()
.build()
.inject(this);
// 打印测试消息
Timber.i(mApple.getDescription());
}
}
运行项目,可以看到,在日志中已经打印出来了我们Apple
类中的信息(这里使用的是JakeWharton
大神的日志打印框架,你直接用 Log.i 打印就好)。
好了,到这里,我们算是将Dagger2 的一个流程整个跑完了,但这只是一种注入的方式,也就是直接通过在类的构造函数上面添加@Inject注解来完成依赖注入,在下一篇文章中我将会为大家讲解两一种提供对象的方式:@Module
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。