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。
Gank 项目中依赖关系比较简单,主要是提供一个单例的 GankService 依赖用于拉取 API 接口。依赖关系图如下:
其中 AppComponent 持有单例的 GankService 依赖,三个 Activity 对应三个 SubComponent 继承自 AppComponent,六个 Fragment 对应六个 SubComponent 继承自 MainActivityComponent。
相应的 Dagger 2 类结构如下:
AppModule 提供 GankService 依赖,ActivityBindModule 定义三个 Activity 对应的 SubComponent 的继承关系,FragemntBindModule 定义六个 Fragment 对应的 SubComponent 的继承关系。
继承关系的实现需要:(1)在 parent Component 依赖的 Module 中的subcomponents
加上 SubComponent 的 class;(2)在 parent Component 中提供返回 SubComponent.Builder 的接口用以创建 SubComponent。
ActivityBindModule 和 FragemntBindModule 对应上面的第一步,下面看看 AppComponent 和 MainActivityComponent 两个关键 Component 的实现:
@Singleton
@Component(modules = [AppModule::class, ActivityBindModule::class])
interface AppComponent {
val appContext: Context
fun mainActivityComponent(): MainActivityComponent.Builder
fun pictureActivityComponent(): PictureActivityComponent.Builder
fun searchActivityComponent(): SearchActivityComponent.Builder
}
@ActivityScope
@Subcomponent(modules = [FragmentBindModule::class])
interface MainActivityComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance
fun activity(activity: Activity): Builder
fun build(): MainActivityComponent
}
fun welfareFragmentComponent(): WelfareFragmentComponent.Builder
fun todayGankFragmentComponent(): TodayGankFragmentComponent.Builder
fun androidFragmentComponent(): AndroidFragmentComponent.Builder
fun iosFragmentComponent(): IOSFragmentComponent.Builder
fun frontEndFragmentComponent(): FrontEndFragmentComponent.Builder
fun videoFragmentComponent(): VideoFramentComponent.Builder
}
先看 AppComponent 的创建过程:
class GankApp : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
...
initInjector()
}
private fun initInjector() {
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
}
再看 SearchActivity 中的注入实现:
class SearchActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
initInjector()
}
private fun initInjector() {
(application as GankApp).appComponent
.searchActivityComponent()
.activity(this)
.build()
.inject(this)
}
}
上面的实现有下面几个问题:
每个需要注入依赖的页面 Activity 或 Fragment 都需要创建一个 Component 类。
继承关系中第二步实现,每个 SubComponent 都需要在 parent componenet 声明对应的返回对应的 SubComponent.Builder 的接口
在 Activity 或 Fragment 中注入依赖时,都必须知道其对应的注入器(Component)的类型,这有悖于依赖注入的原则:被注入的类不应该知道依赖注入的任何细节。
dagger.android 扩展库就是为了解决上述问题而产生的,简化 Dagger 2 在 Android 的使用。
// dagger 2
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
// dagger.android
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
从上面可以看出 dagger.android 扩展库有单独的注解处理器 dagger-android-processor。
以 SearchActivity 为例,说明 dagger.android 的使用:
1.在 AppComponent 中安装 AndroidInjectionModule,确保包含四大组件和 Fragment 的注入器类型。
@Singleton
@Component(modules = [AppModule::class, AndroidInjectionModule::class])
interface AppComponent { ... }
2.Activity 对应的 SubComponent 实现 AndroidInjector
@ActivityScope
@Subcomponent
interface SearchActivitySubcomponent : AndroidInjector<SearchActivity> {
@Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<SearchActivity>()
}
3.在定义 SubComponent 后,添加一个 ActivityBindModule 用来绑定 subcomponent builder,并把该 module 安装到 AppComponent 中。
@Module(subcomponents = [SearchActivitySubcomponent::class])
abstract class ActivityBindModule {
@Binds
@IntoMap
@ActivityKey(SearchActivity::class)
abstract fun bindAndroidInjectorFactory(
builder: SearchActivitySubcomponent.Builder): AndroidInjector.Factory<out Activity>
}
@Singleton
@Component(modules = [AppModule::class, AndroidInjectionModule::class, ActivityBindModule::class])
interface AppComponent { ... }
如果 SubComponent 和其 Builder 没有其他方法或没有继承其他类型,可以使用 @ContributesAndroidInjector 注解简化第二步和第三步,在一个抽象 Module 中添加一个使用 @ContributesAndroidInjector 注解标记的返回具体的 Activity 类型的抽象方法,还可以在 ContributesAndroidInjector 注解中标明 SubComponent 需要安装的 module。如果 SubComponent 需要作用域,只需要标记在该方法上即可。
@Module
abstract class ActivityBindModule {
@ActivityScope
@ContributesAndroidInjector
abstract fun searchActivityInjector(): SearchActivity
}
@Singleton
@Component(modules = [AppModule::class, AndroidInjectionModule::class, ActivityBindModule::class])
interface AppComponent { ... }
4.Application 类实现 HasActivityInjector 接口,并且注入一个 DispatchingAndroidInjector
class GankApp : Application(), HasActivityInjector {
@Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
.inject(this)
}
override fun activityInjector() = dispatchingActivityInjector
}
5.最后在 onCreate)() 方法中,在 super.onCreate()
之前调用 AndroidInjection.inject(this)
。
class SearchActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
...
}
}
使用 dagger.android 后 Activity 对应的 SubComponent 的定义简化如下:
@Module
abstract class ActivityBindModule {
@ActivityScope
@ContributesAndroidInjector(modules = [FragmentBindModule::class])
abstract fun mainActivityInjector(): MainActivity
@ActivityScope
@ContributesAndroidInjector
abstract fun pictureActivityInjector(): PictureActivity
@ActivityScope
@ContributesAndroidInjector
abstract fun searchActivityInjector(): SearchActivity
}
其中 @ContributesAndroidInjector 注解可以解决之前的两个问题,不需要手动创建每一个 SubComponent,也不用在 parent componenet 声明对应的返回对应的 SubComponent.Builder 的接口,以后添加 SubComponent 只需要添加一个 ContributesAndroidInjector 抽象方法。
然后使用AndroidInjection.inject(this)
来简化注入依赖的过程,隐藏注入依赖的细节。
注入 Fragment 和其他三大组件也是类似,只是 ContributesAndroidInjector 抽象方法的返回值变了,HasActivityInjector 接口换做 HasFragmentInjector 等接口。
在上面使用 dagger.android 扩展库注入 Activity 中依赖时,其中第二步定义 SubComponent 和第三步添加一个 SubComponent.Builder 的绑定到 Map 中,这两步可以用 ContributesAndroidInjector 抽象方法简化。其实只是 dagger.android 的注解处理器根据 ContributesAndroidInjector 抽象方法在编译时完成了这两步的代码,编译完后的代码逻辑其实是一样。
看上面 SearchActivity 的例子,ContributesAndroidInjector 抽象方法为:
@Module
abstract class ActivityBindModule {
@ActivityScope
@ContributesAndroidInjector
abstract fun searchActivityInjector(): SearchActivity
}
在编译后会生成一个 ActivityBindModule_SearchActivityInjector 类:
@Module(subcomponents = ActivityBindModule_SearchActivityInjector.SearchActivitySubcomponent.class)
public abstract class ActivityBindModule_SearchActivityInjector {
private ActivityBindModule_SearchActivityInjector() {}
@Binds
@IntoMap
@ActivityKey(SearchActivity.class)
abstract AndroidInjector.Factory extends Activity> bindAndroidInjectorFactory(
SearchActivitySubcomponent.Builder builder);
// 第三步添加一个 SubComponent.Builder 的绑定到 Map 中
@Subcomponent
@ActivityScope
public interface SearchActivitySubcomponent extends AndroidInjector<SearchActivity> {
// 第二步定义 SubComponent
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<SearchActivity> {}
}
}
所以 @ContributesAndroidInjector 原理是利用注解处理器减少手动 coding 的代码量。
dagger.android 扩展库可以使用一行代码AndroidInjection.inject(this)
完成依赖注入的过程,这背后是如何实现的呢?
先看该方法的关键源码:
public static void inject(Activity activity) {
...
// 调用 application 的 activityInjector() 方法获取到 dispatchingActivityInjector 对象
AndroidInjector activityInjector =
((HasActivityInjector) application).activityInjector();
...
// 使用 dispatchingActivityInjector 对象注入 activity 所需的依赖
activityInjector.inject(activity);
}
接着看 DispatchingAndroidInjector 的 inject() 方法的逻辑:
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
private final Map, Provider>>
injectorFactories;
@Inject
DispatchingAndroidInjector(
Map, Provider>> injectorFactories) {
this.injectorFactories = injectorFactories;
}
@Override
public void inject(T instance) {
// 在 maybeInject 中完成注入
boolean wasInjected = maybeInject(instance);
if (!wasInjected) {
throw new IllegalArgumentException(errorMessageSuggestions(instance));
}
}
public boolean maybeInject(T instance) {
// 根据 activity 的类型获取到对应的 AndroidInjector.Factory 的 Provider,
// 其实就是 SubComponent.Builder,因为第二步中定义的 SubComponent.Builder 继承了 AndroidInjector.Factory
Provider> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
@SuppressWarnings("unchecked")
AndroidInjector.Factory factory = (AndroidInjector.Factory) factoryProvider.get();
// 获取到 AndroidInjector.Factory
try {
// 根据 AndroidInjector.Factory 创建 AndroidInjector,即创建 SubComponent
AndroidInjector injector =
checkNotNull(
factory.create(instance),
"%s.create(I) should not return null.",
factory.getClass().getCanonicalName());
injector.inject(instance);
return true;
} catch (ClassCastException e) {
...
}
}
...
}
上面的逻辑简单的来说就是根据 activity 的类型获取相应的 SubComponent.Builder,然后创建其 SubComponent,最后使用 SubComponent 完成依赖注入工作。
现在再回过头来看第三步添加一个 SubComponent.Builder 的绑定到 Map 中,是添加到 AndroidInjectionModule 的 multibinging 中。
@Module
public abstract class AndroidInjectionModule {
@Multibinds
abstract Map, AndroidInjector.Factory extends Activity>>
activityInjectorFactories();
...
}
然后才能实现 DispatchingAndroidInjector
dagger.android 扩展库可以极大地简化在 Android 项目中使用 Dagger 2 的过程,但是还是有些限制,SubComponent.Builder 不能自定义 @BindsInstance 方法,SubCompoennt 的 Module 不能有含参数的构造函数,否则AndroidInjection.inject(this)
在创建 SubComponent 时无法成功。
在使用过 dagger.android 扩展库一段时间后,个人认为其设计非常优雅,简化了 SubComponent 的定义过程和依赖注入的过程,使得开发者可以专注于 Module 管理依赖对象,所以建议大家在 Android 项目中使用。
最后 Dagger 2 系列文章也到此结束了(Dagger 2 关于单元测试的部分放到 Kotlin 下单元测试的系列中),希望对大家有所帮助。
参考资料:
Dagger 2 官网的 Dagger & Android 文档
Dagger 2 官网的 Multibindings 文档