Dagger 2 完全解析系列:
Dagger 2 完全解析(一),Dagger 2 的基本使用與原理
Dagger 2 完全解析(二),進(jìn)階使用 Lazy、Qualifier、Scope 等
Dagger 2 完全解析(三),Component 的組織關(guān)系與 SubComponent
Dagger 2 完全解析(四),Android 中使用 Dagger 2
Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
Dagger 2 完全解析(六),dagger.android 擴(kuò)展庫的使用
本系列文章是基于 Google Dagger 2.11-rc2 版本
在項(xiàng)目中使用了 dagger.android 擴(kuò)展庫后,體驗(yàn)到了其可以簡(jiǎn)化 Dagger 2 在 Android 項(xiàng)目中的使用,所以該系列的最后一篇文章解析 dagger.android 擴(kuò)展庫的使用及原理。本文以個(gè)人寫的 Gank 項(xiàng)目 為例,逐步分析 dagger.android 擴(kuò)展庫的使用。
注:本文代碼語言為 Kotlin。
1. Gank 項(xiàng)目中原來的 Dagger 2 依賴注入邏輯
Gank 項(xiàng)目中依賴關(guān)系比較簡(jiǎn)單,主要是提供一個(gè)單例的 GankService 依賴用于拉取 API 接口。依賴關(guān)系圖如下:

其中 AppComponent 持有單例的 GankService 依賴,三個(gè) Activity 對(duì)應(yīng)三個(gè) SubComponent 繼承自 AppComponent,六個(gè) Fragment 對(duì)應(yīng)六個(gè) SubComponent 繼承自 MainActivityComponent。
相應(yīng)的 Dagger 2 類結(jié)構(gòu)如下:

AppModule 提供 GankService 依賴,ActivityBindModule 定義三個(gè) Activity 對(duì)應(yīng)的 SubComponent 的繼承關(guān)系,F(xiàn)ragemntBindModule 定義六個(gè) Fragment 對(duì)應(yīng)的 SubComponent 的繼承關(guān)系。
1.1 繼承關(guān)系的代碼實(shí)現(xiàn)
繼承關(guān)系的實(shí)現(xiàn)需要:(1)在 parent Component 依賴的 Module 中的subcomponents加上 SubComponent 的 class;(2)在 parent Component 中提供返回 SubComponent.Builder 的接口用以創(chuàng)建 SubComponent。
ActivityBindModule 和 FragemntBindModule 對(duì)應(yīng)上面的第一步,下面看看 AppComponent 和 MainActivityComponent 兩個(gè)關(guān)鍵 Component 的實(shí)現(xiàn):
@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
}
1.2 Component 依賴注入的實(shí)現(xiàn)
先看 AppComponent 的創(chuàng)建過程:
class GankApp : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
...
initInjector()
}
private fun initInjector() {
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
}
再看 SearchActivity 中的注入實(shí)現(xiàn):
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)
}
}
上面的實(shí)現(xiàn)有下面幾個(gè)問題:
每個(gè)需要注入依賴的頁面 Activity 或 Fragment 都需要?jiǎng)?chuàng)建一個(gè) Component 類。
繼承關(guān)系中第二步實(shí)現(xiàn),每個(gè) SubComponent 都需要在 parent componenet 聲明對(duì)應(yīng)的返回對(duì)應(yīng)的 SubComponent.Builder 的接口
在 Activity 或 Fragment 中注入依賴時(shí),都必須知道其對(duì)應(yīng)的注入器(Component)的類型,這有悖于依賴注入的原則:被注入的類不應(yīng)該知道依賴注入的任何細(xì)節(jié)。
2. dagger.android 擴(kuò)展庫的使用
dagger.android 擴(kuò)展庫就是為了解決上述問題而產(chǎn)生的,簡(jiǎn)化 Dagger 2 在 Android 的使用。
2.1 引入 dagger.android 擴(kuò)展庫
// 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 擴(kuò)展庫有單獨(dú)的注解處理器 dagger-android-processor。
2.2 注入 Activity 中的依賴
以 SearchActivity 為例,說明 dagger.android 的使用:
1.在 AppComponent 中安裝 AndroidInjectionModule,確保包含四大組件和 Fragment 的注入器類型。
@Singleton
@Component(modules = [AppModule::class, AndroidInjectionModule::class])
interface AppComponent { ... }
2.Activity 對(duì)應(yīng)的 SubComponent 實(shí)現(xiàn) AndroidInjector<XXActivity> 接口,對(duì)應(yīng)的 @Subcomponent.Builder 繼承 AndroidInjector.Builder<XXActivity>。
@ActivityScope
@Subcomponent
interface SearchActivitySubcomponent : AndroidInjector<SearchActivity> {
@Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<SearchActivity>()
}
3.在定義 SubComponent 后,添加一個(gè) 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 注解簡(jiǎn)化第二步和第三步,在一個(gè)抽象 Module 中添加一個(gè)使用 @ContributesAndroidInjector 注解標(biāo)記的返回具體的 Activity 類型的抽象方法,還可以在 ContributesAndroidInjector 注解中標(biāo)明 SubComponent 需要安裝的 module。如果 SubComponent 需要作用域,只需要標(biāo)記在該方法上即可。
@Module
abstract class ActivityBindModule {
@ActivityScope
@ContributesAndroidInjector
abstract fun searchActivityInjector(): SearchActivity
}
@Singleton
@Component(modules = [AppModule::class, AndroidInjectionModule::class, ActivityBindModule::class])
interface AppComponent { ... }
4.Application 類實(shí)現(xiàn) HasActivityInjector 接口,并且注入一個(gè) DispatchingAndroidInjector<Activity> 類型的依賴作為 activityInjector() 方法的返回值。
class GankApp : Application(), HasActivityInjector {
@Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
.inject(this)
}
override fun activityInjector() = dispatchingActivityInjector
}
5.最后在 onCreate)() 方法中,在 super.onCreate() 之前調(diào)用 AndroidInjection.inject(this)。
class SearchActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
...
}
}
使用 dagger.android 后 Activity 對(duì)應(yīng)的 SubComponent 的定義簡(jiǎn)化如下:
@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 注解可以解決之前的兩個(gè)問題,不需要手動(dòng)創(chuàng)建每一個(gè) SubComponent,也不用在 parent componenet 聲明對(duì)應(yīng)的返回對(duì)應(yīng)的 SubComponent.Builder 的接口,以后添加 SubComponent 只需要添加一個(gè) ContributesAndroidInjector 抽象方法。
然后使用AndroidInjection.inject(this) 來簡(jiǎn)化注入依賴的過程,隱藏注入依賴的細(xì)節(jié)。
注入 Fragment 和其他三大組件也是類似,只是 ContributesAndroidInjector 抽象方法的返回值變了,HasActivityInjector 接口換做 HasFragmentInjector 等接口。
3. dagger.android 擴(kuò)展庫的原理
3.1 @ContributesAndroidInjector 原理
在上面使用 dagger.android 擴(kuò)展庫注入 Activity 中依賴時(shí),其中第二步定義 SubComponent 和第三步添加一個(gè) SubComponent.Builder 的綁定到 Map 中,這兩步可以用 ContributesAndroidInjector 抽象方法簡(jiǎn)化。其實(shí)只是 dagger.android 的注解處理器根據(jù) ContributesAndroidInjector 抽象方法在編譯時(shí)完成了這兩步的代碼,編譯完后的代碼邏輯其實(shí)是一樣。
看上面 SearchActivity 的例子,ContributesAndroidInjector 抽象方法為:
@Module
abstract class ActivityBindModule {
@ActivityScope
@ContributesAndroidInjector
abstract fun searchActivityInjector(): SearchActivity
}
在編譯后會(huì)生成一個(gè) 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);
// 第三步添加一個(gè) SubComponent.Builder 的綁定到 Map 中
@Subcomponent
@ActivityScope
public interface SearchActivitySubcomponent extends AndroidInjector<SearchActivity> {
// 第二步定義 SubComponent
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<SearchActivity> {}
}
}
所以 @ContributesAndroidInjector 原理是利用注解處理器減少手動(dòng) coding 的代碼量。
3.2 AndroidInjection.inject(this) 的原理
dagger.android 擴(kuò)展庫可以使用一行代碼AndroidInjection.inject(this)完成依賴注入的過程,這背后是如何實(shí)現(xiàn)的呢?
先看該方法的關(guān)鍵源碼:
public static void inject(Activity activity) {
...
// 調(diào)用 application 的 activityInjector() 方法獲取到 dispatchingActivityInjector 對(duì)象
AndroidInjector<Activity> activityInjector =
((HasActivityInjector) application).activityInjector();
...
// 使用 dispatchingActivityInjector 對(duì)象注入 activity 所需的依賴
activityInjector.inject(activity);
}
接著看 DispatchingAndroidInjector 的 inject() 方法的邏輯:
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
private final Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>>
injectorFactories;
@Inject
DispatchingAndroidInjector(
Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> 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) {
// 根據(jù) activity 的類型獲取到對(duì)應(yīng)的 AndroidInjector.Factory 的 Provider,
// 其實(shí)就是 SubComponent.Builder,因?yàn)榈诙街卸x的 SubComponent.Builder 繼承了 AndroidInjector.Factory
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
@SuppressWarnings("unchecked")
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
// 獲取到 AndroidInjector.Factory
try {
// 根據(jù) AndroidInjector.Factory 創(chuàng)建 AndroidInjector,即創(chuàng)建 SubComponent
AndroidInjector<T> injector =
checkNotNull(
factory.create(instance),
"%s.create(I) should not return null.",
factory.getClass().getCanonicalName());
injector.inject(instance);
return true;
} catch (ClassCastException e) {
...
}
}
...
}
上面的邏輯簡(jiǎn)單的來說就是根據(jù) activity 的類型獲取相應(yīng)的 SubComponent.Builder,然后創(chuàng)建其 SubComponent,最后使用 SubComponent 完成依賴注入工作。
現(xiàn)在再回過頭來看第三步添加一個(gè) SubComponent.Builder 的綁定到 Map 中,是添加到 AndroidInjectionModule 的 multibinging 中。
@Module
public abstract class AndroidInjectionModule {
@Multibinds
abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
activityInjectorFactories();
...
}
然后才能實(shí)現(xiàn) DispatchingAndroidInjector<Activity> 的注入過程,Application 需要實(shí)現(xiàn) HasActivityInjector 接口,也是為了方便獲取 dispatchingActivityInjector 對(duì)象,這樣就隱藏了注入器的類型,通過 Activity 的類型獲取對(duì)應(yīng)的 SubComponent 完成注入。
4. 總結(jié)
dagger.android 擴(kuò)展庫可以極大地簡(jiǎn)化在 Android 項(xiàng)目中使用 Dagger 2 的過程,但是還是有些限制,SubComponent.Builder 不能自定義 @BindsInstance 方法,SubCompoennt 的 Module 不能有含參數(shù)的構(gòu)造函數(shù),否則AndroidInjection.inject(this)在創(chuàng)建 SubComponent 時(shí)無法成功。
在使用過 dagger.android 擴(kuò)展庫一段時(shí)間后,個(gè)人認(rèn)為其設(shè)計(jì)非常優(yōu)雅,簡(jiǎn)化了 SubComponent 的定義過程和依賴注入的過程,使得開發(fā)者可以專注于 Module 管理依賴對(duì)象,所以建議大家在 Android 項(xiàng)目中使用。
最后 Dagger 2 系列文章也到此結(jié)束了(Dagger 2 關(guān)于單元測(cè)試的部分放到 Kotlin 下單元測(cè)試的系列中),希望對(duì)大家有所幫助。
參考資料: