前言
最近在用 MVP + RxJava + Retrofit 写项目,觉得相对于其他的开发框架,这的确是给我们带来了很多方便,但是在网上搜寻相关资料的时候,总是能看到 MVP + RxJava + Retrofit + Dagger 这样的搭配组合,那 Dagger 又是一个怎样的框架呢,我也去具体搜了搜,但看到一些文章带着“Dagger2从入门到放弃”这样意思的句子,就感觉Dagger2会很难吗,emmmm...行吧,好像是有点难理解,但是想着既然有那么多人用这个框架,必然有它的好处,于是花了些时间学习了一波。
Dagger2
Dagger2 : A fast dependency injector for Android and Java. (官方给出的定义)
Dagger2是一个依赖注解框架,它的作用就是通过注解来实现组件之间的依赖,由此来达到更好的解耦,比如说,如果将 Dagger2 使用到我们的 MVP 框架中,那么我们就可以达到 V层 和 P层 的进一步解耦,从而使我们的项目有更好的维护性。
Dagger2 和 Butterknife 一样,都是在编译阶段利用Java注解通过APT(Annotation Processing Tool)自动生成Java代码,然后由我们自己写的代码进行调用,完成依赖注入,因此不用担心性能上的问题。那么如何来使用 Dagger2 呢?我们先来看一下使用 Dagger2 和不使用的区别。
对比
用简单的例子来说明。比如说我们有这样一个AAA类,没什么内容,只有一个get()方法
/** * @author chaochaowu * @Description : AAA * @class : AAA * @time Create at 6/12/2018 10:58 AM */ public class AAA { public AAA() { } public void get(){ Log.e("AAA","has gotten."); } }
我们需要在MainActivity用到这个类,因此MainActivity中变含有这个成员变量,有了这个成员变量aaa,我们就要对它进行实例化,因此会有以下的代码:
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity { AAA aaa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); aaa = new AAA(); aaa.get(); } }
我们需要在 MainActivity 中声明aaa这个变量,然后会对其进行new一个实例,然后才能对其进行调用,调用它定义的方法。这样,AAA类对象的创建就和 MainActivity 耦合在了一起。如果使用 Dagger2 进行依赖注入呢
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity { @Inject AAA aaa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); DaggerMainActivityComponent.create().inject(this); aaa.get(); } }
代码就会变成这样。我们可以发现MainActivity里没有任何有关aaa的初始化赋值,但是它还是可以调用其get()方法。这样我们就达到了解耦合的目的。
这样一个简单的例子可能看不出 Dagger2 的好处,但是你可以想象一下,如果这个 Activity 里有很多个类似 AAA 这样的类的成员变量,它们都需要初始化,有的初始化还要传参数,那么,这个 Activity 就会变得臃肿,如果 AAA类 的初始化参数变了,不仅 AAA类 需要改,MainActivity 因为它与AAA类的耦合也要跟着改变,如果 AAA类 不止用在 MainActivity 中还用在了其他的 Activity 中,那么要改的东西变的可有点多咯,但是如果用上Dagger2,我们就可以很好的解决这个问题。
注解介绍
Dagger2中注解有:@Inject, @Component, @Module, @Provides, @Qulifier, @Scope, @Singleten 。而我们主要用到的是@Inject, @Component,@Module, @Provides 这四个,下面来介绍一下。
@Inject
Inject 主要用来标记需要依赖的变量,告诉Dagger需要为它提供依赖;inject 还被用来标记类的构造函数。当Dagger2碰到使用@Inject注解的变量时,会去寻找这个变量对应的被@Inject注解的构造函数,把实例构造出来,为变量提供依赖。
@Component
@Component用于标注接口,是依赖需求方(MainActivity)和依赖提供方(AAA)之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类,类的名称为被@Component注解的类的名称前加上Dagger,我们通过调用这个实现类的方法来完成注入。
@Module
Module用于标注提供依赖的类。虽然我们有@Inject注解实现注入,但是@Inject只能注入构造函数为无参的类,要是构造函数是带参数的呢?那就要使用module注解来解决这个问题,又比如说,有很多的第三方库,我们对它的代码无法进行修改,也就不能对其构造函数加上@Inject注解,那么可咋办啊,@module注释可以很好的解决我们的问题。
@Provides
Provides是与Module一起使用的,@Provides用于标注Module所标注的类中的方法,该方法会在需要提供依赖时被调用,在方法内进行对象的初始化,返回对象依赖给标注了@Inject的变量。相当于一个有参数的@Inject。
我们来具体的使用一下。
构造函数无参的对象注入
用上面提到的 AAA类 进行依赖注入演示。
首先我们在 AAA类 的构造函数上加上 @Inject 注解(构造函数必须得是无参)
/** * @author chaochaowu * @Description : AAA * @class : AAA * @time Create at 6/12/2018 10:58 AM */ public class AAA { @Inject public AAA() { } public void get(){ Log.e("AAA","has gotten."); } }
然后创建一个 MainActivityComponent 接口并用 @Component 进行注解,然后Build一下项目,
/** * @author chaochaowu * @Description : MainActivityComponent * @class : MainActivityComponent * @time Create at 6/12/2018 11:03 AM */ @Component public interface MainActivityComponent { void inject(MainActivity mainActivity); }
Build之后apt会自动生成如下的代码
build后生成.png
我们利用其中的 DaggerMainActivityComponent 进行 MainActivity 与 AAA类 之间的桥梁搭建。如下代码
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity { @Inject AAA aaa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); DaggerMainActivityComponent.create().inject(this); aaa.get(); } }
至此,我们便完成了构造函数无参的对象的依赖注入。
构造函数含参的对象注入
相信很多的时候,我们要用的对象在构造的时候是需要参数的,那我们如何进行它们的依赖注入呢,这里我用 MVP 中的 Presenter 来进行演示。
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity implements MainContract.View{ MainContract.Presenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPresenter = new MainPresenter(this); mPresenter.getData(); } }
上面的代码是没用 Dagger2 时的 Activity,在 MVP 框架中,由于 Presenter 需要与 View 进行交互,需要持有View的对象,因此在初始化的时候,需要在构造函数里传入View对象作为参数,可以看到代码中 mPresenter = new MainPresenter(this);
接下来我们对它进行依赖注入。首先看一下 Presenter,,Presenter不需要作出任何改变,我们可以看到它的构造函数带有一个参数View
/** * @author chaochaowu * @Description : MainPresenter * @class : MainPresenter * @time Create at 6/11/2018 2:22 PM */ public class MainPresenter implements MainContract.Presenter{ MainContract.View mView; public MainPresenter(MainContract.View mView) { this.mView = mView; } @Override public void getData() { } }
然后我们需要创建一个 MainActivityModule 类,并在该类名加上@Module注解,它含有一个成员变量 mView 需要在构造函数时赋值,我们之后会将这个变量给 Presenter 进行 Presenter 的构造,我们还需要写一个方法,并在其上添加@Provides注解,方法的名字其实没所谓可以随便取,但是为了代码的可读性,一般都以provide开头加上provide的东西的名字,我们在这个方法里返回我们所要提供依赖的对象,这里返回了一个 Presenter 对象,可以看到它在这里的构造,我们传入了参数 View。到这便完成了MainActivityModule 的定义,其实这个 MainActivityModule 作用就和之前的 构造函数无参中的 对无参构造函数 Inject 步骤的性质相同。
/** * @author chaochaowu * @Description : MainActivityModule * @class : MainActivityModule * @time Create at 6/11/2018 2:41 PM */ @Module public class MainActivityModule { MainContract.View mView; public MainActivityModule(MainContract.View mView) { this.mView = mView; } @Provides public MainContract.Presenter providePresenter(){ return new MainPresenter(mView); } }
接下来和 构造函数无参的对象注入 中一样,我们需要定义一个 MainActivityComponent ,并用 @Component 进行注解,不一样的是需要在括号里加上参数 modules = {MainActivityModule.class} 如下代码。
/** * @author chaochaowu * @Description : MainActivityComponent * @class : MainActivityComponent * @time Create at 6/11/2018 2:45 PM */ @Component(modules = {MainActivityModule.class}) public interface MainActivityComponent { void inject(MainActivity mainActivity); }
Build一下项目
build之后生成.png
这样我们就可以像上面一样,在Activity中调用DaggerMainActivityComponent 进行依赖注入。看一下Activity中的代码。
/** * @author chaochaowu * @Description : MainActivity * @class : MainActivity * @time Create at 6/12/2018 10:58 AM */ public class MainActivity extends AppCompatActivity implements MainContract.View{ @Inject MainContract.Presenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.builder() .mainActivityModule(new MainActivityModule(this)) .build() .inject(this); mPresenter.getData(); } }
可以看到这里 DaggerMainActivityComponent 调用的方法有所不同,其中调用了一个mainActivityModule() 方法,传入了一个 MainActivityModule 对象,该对象的初始化,我们又传入了一个this,也就是View,这个 View就是我们在之后创建 Presenter 时传入的参数 。
至此,我们就完成了构造函数有参的对象的依赖注入。其他有参的、第三方库的依赖注入也是通过这样的方式进行。
总结
Dagger2的使用,让我们代码的耦合度进一步降低了,这是一个优点,但是另一方面,使用了Dagger2你需要额外的去定义一些类,导致代码的数量也增加。个人觉得,在比较小的项目中,如果去使用,有点emmmm....多此一举?(划掉),但是如果一个项目比较大,代码本身也比较多,那么使用Dagger2所带来的优点,便可以显现了。所以说,是否在项目中使用 Dagger2 仁者见仁智者见智,不过作为一个学习的点,还是挺不错的,嗯。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。