Android学习进阶——Dagger 2 使用及原理

概述

Dagger 2 是 Google 开源的一款依赖注入框架,它的前身是 square 的 Dagger 1,Dagger 2 在 Android 中有着较为广泛的应用。

Dagger 2 基于 Java 注解,采用 annotationProcessor(注解处理器) 在项目编译时动态生成依赖注入需要的 Java 代码,然后我们在合适的位置手动完成最终的依赖注入,而不是 Dagger 1 中基于反射的解决方案,所以在性能上是有保障的。

Dagger2的使用

首先我先用张图来简单说明一下Dagger2使用的流程


图中的Module是应用中常用的功能模块,比如说网络访问,数据存储等。这个类会有一个@Module的注解,具体的代码我会在后面详细介绍。其实这个Module的作用是提供各种功能对象,而这些Module会放到一个有@Component的容器类中,也就是图中Component类。当我们想使用各种功能对象进行业务操作的时候,只需要这个容器就能得到被注册了的功能对象,图中的意思是在Activity中使用对象进行业务操作,当然也不仅限于Activity。

上图相对来说还是太简略了,并没有完整的表达出Dagger2的原理,下面我们直接从代码中感受一个Dagger2的强大。

1.引入Dagger2

implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'

2.创建网络访问对象及其Module模块

首先创建一个HttpObject,我们假设这个HttpObject中有各种网络的操作,get,post,put等

public void get(){
        Log.i("Dagger2","这里是get方法");
    }
    
    public void post(){
        Log.i("Dagger2","这里是post方法");
    }
}
创建HttpModule

@Module
public class HttpModule {
    @Provides
    public HttpObject providerHttpObject(){
        return new HttpObject();
    }
}

HttpModule的两个注解是需要注意的地方:@Module这个注解相当于给当前类打了一个标记,表明了这个类的类型,便于注入到容器中;@Provides这个注解放在了方法的上面,从上面的代码可以看出来,主要就是创建功能对象。

3.创建容器Component

@Component(modules = {HttpModule.class})
public interface MyComponent {
    void injectMainActivity(MainActivity mainActivity);
}

容器这个类被@Component所注解,而且是一个接口类,@Component中的modules参数接收的类型是一个数组,表示被装入容器的Module有哪些。injectMainActivity方法表示这个容器中的功能对象(例如HttpObject)会在哪个类使用,我这里使用的MainActivity做的测试,所以参数写的是MainActivity。

4.在类中使用HttpObject

在配置完上述的代码之后,一定先rebuild!

public class MainActivity extends AppCompatActivity {
    @Inject
    HttpObject mHttpObject;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMyComponent.create().injectMainActivity(this);
        mHttpObject.get();
        mHttpObject.post();
    }
}

因为我们要使用的类是HttpObject,所以在MainActivity创建这个类的对象,然后被@Inject所注解。要注意的是DaggerMyComponent这个类是rebuild之后生成的,调用DaggerMyComponent.create().inkectMainActivity(this)这句话来生成mHttpObject对象,调用HttpObject中的get和post方法就会有相应的输出。

Dagger 2原理

相比机械性的记住这些注解以及使用流程,Dagger 2 背后的工作原理更加重要,这样我们将明白各个模块的职责,以及框架是如何将它们关联起来帮助我们完成依赖注入的,明白了原理后使用 Dagger 2 就会更加得心应手了。

所以我们这里先了解 Dagger 2 背后的基本原理,然后再学习其它内容。

以前边的demo为例,项目编译后会在app\build\generated\source\apt\debug\包名目录下生成依赖注入的相关类,如下:

按照之前说法,DaggerMainComponent是完成依赖注入的核心,所以从这个类开始分析,它的源码如下:

public final class DaggerMainComponent implements MainComponent {
  private MainModule mainModule;

  private DaggerMainComponent(Builder builder) {
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.mainModule = builder.mainModule;
  }
  // 重写inject方法,完成依赖注入
  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);
  }
  // 完成依赖注入的重要方法
  private MainActivity injectMainActivity(MainActivity instance) {
    // 给 MainActivity 中的 cat 成员变量赋值
    MainActivity_MembersInjector.injectCat(instance, new Cat());
    // 给 MainActivity 中的 flower 成员变量赋值
    MainActivity_MembersInjector.injectFlower1(
        instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
    return instance;
  }
 
  public static final class Builder {
    private MainModule mainModule;

    private Builder() {}
    // 完成DaggerMainComponent对象的创建
    public MainComponent build() {
      if (mainModule == null) {
        this.mainModule = new MainModule();
      }
      return new DaggerMainComponent(this);
    }
    // 设置 mainModule 对象
    public Builder mainModule(MainModule mainModule) {
      this.mainModule = Preconditions.checkNotNull(mainModule);
      return this;
    }
  }
}

典型的 Builder 构建模式,再结合之前 DaggerMainComponent 的用法来分析:

DaggerMainComponent.builder()
                .mainModule(new MainModule())
                .build()
                .inject(this);

先通过builder()方法创建一个 Builder 对象,再通过其mainModule()方法设置mainModule 对象,接下来用 build()方法就是创建DaggerMainComponent 对象,这样它里边也有了一个mainModule对象。

在 DaggerMainComponent 中还重写了 MainComponent 接口的inject()方法,里边调用的injectMainActivity()方法是完成依赖注入的关键:

private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectCat(instance, new Cat());
    MainActivity_MembersInjector.injectFlower1(
        instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
    return instance;
  }

首先是调用MainActivity_MembersInjector类的injectCat()方法直接创建一个 Cat 对象,完成 MainActivity 中 cat 的赋值,injectCat()方法声明如下:

public static void injectCat(MainActivity instance, Cat cat) {
    instance.cat = cat;
  }

然后是调用injectFlower()方法,完成 MainActivity 中 flower 的赋值,那么 flower 对象的值从哪里来呢?这里调用了MainModule_ProvideRedRoseFactory的proxyProvideRedRose()方法:

public static Flower proxyProvideRedRose(MainModule instance) {
    return Preconditions.checkNotNull(
        instance.provideRedRose(), "Cannot return null from a non-@Nullable @Provides method");
  }

里边最终是调用了我们在 MainModule 中声明的 provideRedRose() 方法,所以在 DaggerMainComponent 内部是通过 MainActivity_MembersInjector 完成了最终的依赖注入。所以当在 Activity 中执行inject(this)方法时,就是开始创建依赖对象,并完成注入工作。

到这里整个依赖注入的流程就结束了,从源码的角度来看,整个过程更像是医生给患者注射药物。我们可以把依赖注入组件 DaggerMainComponent 看做“医生”,把 MainActivity_MembersInjector 看做“注射器”,MainModule 就是“药物”,MainActivity 就是“患者”,医生用注射器把药物送到患者体内。

Dagger的优点

有的小伙伴会问,你整这么一大堆是为了啥?要不然直接创建对象,要不然创建一个单例,多省事,可比Dagger2的这种方式方便多了。在中大型项目中,类似于HttpObject这种对象会被大量的应用,如果突然有一天这个类的初始化方法改变了,你岂不是要修改每一处吗。即便是使用单例getInstance方法也避免不了这种问题,因为如果在创建对象的时候需要在构造器中添加一个参数,每一处的getInstance也需要被修改。而Dagger2完美的避免了这种问题。


以上就是Android中Dagger 2的基本使用以及原理的学习,有关Android开发中还有更多的学习技术;这里推荐参考《Android核心技术手册》这个大文档里面记录了30多个技术板块。几千个小知识点带你引进更高的技术层面。

总结

Dagger 2 的主要内容就这些了,使用 Dagger 2 必然要编写相关的辅助类、接口、使用各种注解,虽然没有直接 new 一个对象或者传统的依赖注入方式简单,但 Dagger 2 带来的是更好的代码解耦,更有利于后期的扩展维护,对于那些需要长期维护的项目这一点是更加重要的。

推荐阅读更多精彩内容