Dagger2 Android依赖注入学习笔记

作者:袖梨 2022-06-25

前言

最近在用 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 仁者见仁智者见智,不过作为一个学习的点,还是挺不错的,嗯。

相关文章

精彩推荐