Dagger 2 完全解析系列:
Dagger 2 完全解析(一),Dagger 2 的基本使用与原理
Dagger 2 完全解析(二),进阶使用 Lazy、Qualifier、Scope 等
Dagger 2 完全解析(三),Component 的组织关系与 SubComponent
Dagger 2 完全解析(四),Android 中使用 Dagger 2
Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
Dagger 2 完全解析(六),dagger.android 扩展库的使用
本系列文章是基于 Google Dagger 2.11-rc2 版本
在项目中使用了 dagger.android 扩展库后,体验到了其可以简化 Dagger 2 在 Android 项目中的使用,所以该系列的最后一篇文章解析 dagger.android 扩展库的使用及原理。本文以个人写的 Gank 项目 为例,逐步分析 dagger.android 扩展库的使用。
注:本文代码语言为 Kotlin。
1. Gank 项目中原来的 Dagger 2 依赖注入逻辑
Gank 项目中依赖关系比较简单,主要是提供一个单例的 GankService 依赖用于拉取 API 接口。依赖关系图如下:
其中 AppComponent 持有单例的 GankService 依赖,三个 Activity 对应三个 SubComponent 继承自 AppComponent,六个 Fragment 对应六个 SubComponent 继承自 MainActivityComponent。
相应的 Dagger 2 类结构如下:
AppModule 提供 GankService 依赖,ActivityBindModule 定义三个 Activity 对应的 SubComponent 的继承关系,FragemntBindModule 定义六个 Fragment 对应的 SubComponent 的继承关系。
1.1 继承关系的代码实现
继承关系的实现需要:(1)在 parent Component 依赖的 Module 中的subcomponents
加上 SubComponent 的 class;(2)在 parent Component 中提供返回 SubComponent.Builder 的接口用以创建 SubComponent。
ActivityBindModule 和 FragemntBindModule 对应上面的第一步,下面看看 AppComponent 和 MainActivityComponent 两个关键 Component 的实现:
|
|
1.2 Component 依赖注入的实现
先看 AppComponent 的创建过程:
|
|
再看 SearchActivity 中的注入实现:
|
|
上面的实现有下面几个问题:
每个需要注入依赖的页面 Activity 或 Fragment 都需要创建一个 Component 类。
继承关系中第二步实现,每个 SubComponent 都需要在 parent componenet 声明对应的返回对应的 SubComponent.Builder 的接口
在 Activity 或 Fragment 中注入依赖时,都必须知道其对应的注入器(Component)的类型,这有悖于依赖注入的原则:被注入的类不应该知道依赖注入的任何细节。
2. dagger.android 扩展库的使用
dagger.android 扩展库就是为了解决上述问题而产生的,简化 Dagger 2 在 Android 的使用。
2.1 引入 dagger.android 扩展库
|
|
从上面可以看出 dagger.android 扩展库有单独的注解处理器 dagger-android-processor。
2.2 注入 Activity 中的依赖
以 SearchActivity 为例,说明 dagger.android 的使用:
1.在 AppComponent 中安装 AndroidInjectionModule,确保包含四大组件和 Fragment 的注入器类型。
|
|
2.Activity 对应的 SubComponent 实现 AndroidInjector
|
|
3.在定义 SubComponent 后,添加一个 ActivityBindModule 用来绑定 subcomponent builder,并把该 module 安装到 AppComponent 中。
|
|
如果 SubComponent 和其 Builder 没有其他方法或没有继承其他类型,可以使用 @ContributesAndroidInjector 注解简化第二步和第三步,在一个抽象 Module 中添加一个使用 @ContributesAndroidInjector 注解标记的返回具体的 Activity 类型的抽象方法,还可以在 ContributesAndroidInjector 注解中标明 SubComponent 需要安装的 module。如果 SubComponent 需要作用域,只需要标记在该方法上即可。
|
|
4.Application 类实现 HasActivityInjector 接口,并且注入一个 DispatchingAndroidInjector
|
|
5.最后在 onCreate)() 方法中,在 super.onCreate()
之前调用 AndroidInjection.inject(this)
。
|
|
使用 dagger.android 后 Activity 对应的 SubComponent 的定义简化如下:
|
|
其中 @ContributesAndroidInjector 注解可以解决之前的两个问题,不需要手动创建每一个 SubComponent,也不用在 parent componenet 声明对应的返回对应的 SubComponent.Builder 的接口,以后添加 SubComponent 只需要添加一个 ContributesAndroidInjector 抽象方法。
然后使用AndroidInjection.inject(this)
来简化注入依赖的过程,隐藏注入依赖的细节。
注入 Fragment 和其他三大组件也是类似,只是 ContributesAndroidInjector 抽象方法的返回值变了,HasActivityInjector 接口换做 HasFragmentInjector 等接口。
3. dagger.android 扩展库的原理
3.1 @ContributesAndroidInjector 原理
在上面使用 dagger.android 扩展库注入 Activity 中依赖时,其中第二步定义 SubComponent 和第三步添加一个 SubComponent.Builder 的绑定到 Map 中,这两步可以用 ContributesAndroidInjector 抽象方法简化。其实只是 dagger.android 的注解处理器根据 ContributesAndroidInjector 抽象方法在编译时完成了这两步的代码,编译完后的代码逻辑其实是一样。
看上面 SearchActivity 的例子,ContributesAndroidInjector 抽象方法为:
|
|
在编译后会生成一个 ActivityBindModule_SearchActivityInjector 类:
|
|
所以 @ContributesAndroidInjector 原理是利用注解处理器减少手动 coding 的代码量。
3.2 AndroidInjection.inject(this) 的原理
dagger.android 扩展库可以使用一行代码AndroidInjection.inject(this)
完成依赖注入的过程,这背后是如何实现的呢?
先看该方法的关键源码:
|
|
接着看 DispatchingAndroidInjector 的 inject() 方法的逻辑:
|
|
上面的逻辑简单的来说就是根据 activity 的类型获取相应的 SubComponent.Builder,然后创建其 SubComponent,最后使用 SubComponent 完成依赖注入工作。
现在再回过头来看第三步添加一个 SubComponent.Builder 的绑定到 Map 中,是添加到 AndroidInjectionModule 的 multibinging 中。
|
|
然后才能实现 DispatchingAndroidInjector
4. 总结
dagger.android 扩展库可以极大地简化在 Android 项目中使用 Dagger 2 的过程,但是还是有些限制,SubComponent.Builder 不能自定义 @BindsInstance 方法,SubCompoennt 的 Module 不能有含参数的构造函数,否则AndroidInjection.inject(this)
在创建 SubComponent 时无法成功。
在使用过 dagger.android 扩展库一段时间后,个人认为其设计非常优雅,简化了 SubComponent 的定义过程和依赖注入的过程,使得开发者可以专注于 Module 管理依赖对象,所以建议大家在 Android 项目中使用。
最后 Dagger 2 系列文章也到此结束了(Dagger 2 关于单元测试的部分放到 Kotlin 下单元测试的系列中),希望对大家有所帮助。
参考资料: