使用Kotlin構(gòu)建MVVM應(yīng)用程序—提高篇:Dagger-Android

寫在前面

提高篇的目的是想著寫一些較深入的知識(shí),包含一些源碼分析、架構(gòu)設(shè)想腳手架搭建的東西。
面向的人群是中高級的開發(fā)者或者愿意深入了解如何快速構(gòu)建Kotlin&&MVVM應(yīng)用的人群。

Dagger-Android

原本的打算是將其作為使用Kotlin構(gòu)建MVVM應(yīng)用程序系列的第五部分內(nèi)容。
但因?yàn)镈agger本身就有一定的入門門檻,Dagger-Android的門檻就更高了。對于初中級開發(fā)者而言,Dagger-Android太容易入門到放棄,對于這部分人群不是很適合,因此將其放入提高篇較為合適。
又因?yàn)镈agger-Android門檻較高,對于初學(xué)者來說不適用,就如同初出江湖的小菜鳥就想著去練習(xí)遠(yuǎn)高于自己根基的武功,對自己沒有太多好處,多積累基礎(chǔ),設(shè)計(jì)模式才是正途,能力到了自然就悟了。
又由于曲高和寡,懂的人自然懂。為此,我開通了使用Kotlin構(gòu)建MVVM應(yīng)用程序的小專欄,提高篇的完整內(nèi)容會(huì)放在這里,愿意去了解的了解一哈。
而這里也就大致的總結(jié)一下它的思路。

為什么要有Dagger-Android?

對于這個(gè)問題,google在Dagger-Android的文檔上有解釋:

我們普通的dagger代碼如下所示:

public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // DO THIS FIRST. Otherwise frombulator might be null!
    ((SomeApplicationBaseType) getContext().getApplicationContext())
        .getApplicationComponent()
        .newActivityComponentBuilder()
        .activity(this)
        .build()
        .inject(this);
    // ... now you can write the exciting code
  }
}

這會(huì)帶來一些問題:

  1. 只是復(fù)制粘貼上面的代碼會(huì)讓以后的重構(gòu)比較困難,還會(huì)讓一些開發(fā)者不知道Dagger到底是如何進(jìn)行注入的(然后就更不易理解了)
  2. 更重要的原因是:它要求注射類型(FrombulationActivity)知道其注射器。 即使這是通過接口而不是具體類型完成的,它打破了依賴注入的核心原則:一個(gè)類不應(yīng)該知道如何實(shí)現(xiàn)依賴注入。

為了解決上述的問題,Dagger-Android應(yīng)運(yùn)而生。
這是它的起因,那么和傳統(tǒng)的Dagger區(qū)別又在哪里呢?

區(qū)別在哪?

就在解法不同罷了,如果你認(rèn)為Dagger-Android是普通Dagger的延伸,要按照Dagger的邏輯去理解Dagger-Android,那么一開始就錯(cuò)了。
假設(shè)把依賴注入看成是一道算法題或者數(shù)學(xué)的最后一道大題。
Dagger和Dagger-Android都是正確的答案,只是兩個(gè)不同的解法而已。
相比之下,Dagger-Android在程序啟動(dòng)的時(shí)候通過處理注解,初始化了一個(gè)全局的單例map

Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>>

key值為activity/fragment的class,而value為提供相應(yīng)Component的Provider對象。在開始的時(shí)候就為我們映射好了。
當(dāng)我們調(diào)用AndroidInjection.inject(this)時(shí),也就是一個(gè)簡單的map.get(instance.class).getComponent().inject(this)
相比之下,多了一個(gè)從map去獲取component的中間過程,但是卻解決了上訴的第二個(gè)問題 :一個(gè)類不應(yīng)該知道如何實(shí)現(xiàn)依賴注入。

思路的差別就在這里,明白這一點(diǎn),通過斷點(diǎn)一步步的源碼分析,Dagger-Android也沒有多么高不可攀。

快速開始

首先我們需要在app/build.gradle加入相應(yīng)的依賴

    //dagger2  di
    implementation 'com.google.dagger:dagger:2.16'
    kapt 'com.google.dagger:dagger-compiler:2.16'
    //dagger-android
    implementation 'com.google.dagger:dagger-android:2.16'
    implementation 'com.google.dagger:dagger-android-support:2.16' 
    kapt 'com.google.dagger:dagger-android-processor:2.16'

注入方法建議看文檔更好,這里簡單描述一下:

還是以MVVM-Android為例。

  1. 添加ActivityModule.kt
@Module
abstract class ActivityModule {

    @ContributesAndroidInjector
    abstract fun contributePaoActivity(): PaoActivity

}

2 . 修改AppComponent.kt

@Singleton
@Component(modules = arrayOf(
        AndroidInjectionModule::class,
        AppModule::class,
        ActivityModule::class)
)
interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        fun build(): AppComponent
    }

    fun inject(application: PaoApp)
}

相比Dagger2,modules多了AndroidInjectionModule和ActivityModule兩個(gè)類。

  1. rebuild一下項(xiàng)目,然后新增PaoApp.kt同時(shí)實(shí)現(xiàn)HasActivityInjector接口
class PaoApp : Application(),HasActivityInjector{

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.builder().application(app).build().inject(app)
    }

    override fun activityInjector() = dispatchingAndroidInjector

}
  1. 最后在Activity的onCreate()方法之前調(diào)用 AndroidInjection.inject(this)進(jìn)行注入
class PaoActivity : RxAppCompatActivity() {
    @Inject
    lateinit var mViewModel : PaoViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        //////di
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
    }
}

到此,一個(gè)簡單的依賴注入就好了。
當(dāng)然簡單是絕不簡單的。這些是在寫什么?完全云里霧里。剛從Dagger轉(zhuǎn)換成Dagger-Android的直接就勸退了,還不如直接使用Dagger2的好。

確實(shí),對于日常功能迭代的開發(fā)團(tuán)隊(duì)來說,普通的dagger更易理解,所以Dagger-Android也算是一個(gè)可選項(xiàng),可以作為一個(gè)提高,而且google的很多示例里dagger的用法都是Dagger-Android,所以還是有必要懂它的原理。

原理剖析

第四部分中,我們也了解了普通的Dagger是如何進(jìn)行依賴注入的,這里我們再來回顧一次

由AppModule提供所需的依賴
由AppCompent提供注入的途徑
由@Inject標(biāo)識(shí)需要注入的對象
調(diào)用
DaggerAppComponent.builder()
   .appModule(AppModule(applicationContext)).build()
   .inject(this)
完成依賴注入

這里的邏輯比較好理解一些,就是普通的

paoActivity.mViewModel = appComponent.paoViewModel

那Dagger-Android相比之下,又是怎么樣的邏輯呢?

相比之前,Dagger-Android將Activity/Fragment所需的compoent都放到了一個(gè)map對象里,這個(gè)map對象由App的dispatchingAndroidInjector對象持有。其中key值為activity/frament的class,value為提供相應(yīng)Component的Provider對象。

Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>>

當(dāng)我們在Activity中調(diào)用 AndroidInjection.inject(this)時(shí),又在做什么呢?

public static void inject(Activity activity) {
    checkNotNull(activity, "activity");
    Application application = activity.getApplication();
    if (!(application instanceof HasActivityInjector)) {
      throw new RuntimeException(
          String.format(
              "%s does not implement %s",
              application.getClass().getCanonicalName(),
              HasActivityInjector.class.getCanonicalName()));
    }
    //找到dispatchingAndroidInjector對象
    AndroidInjector<Activity> activityInjector =
        ((HasActivityInjector) application).activityInjector();
    checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass());
    //進(jìn)行注入
    activityInjector.inject(activity);
  }

activityInjector.inject(activity)里面的邏輯又可以描述為

一個(gè)全局的單例map對象,通過key值為activity.class即
map.get(activity.class) 找到activity與之對應(yīng)的Provider<AndroidInjector.Factory<? extends T>>
。。。
。。。
然后經(jīng)過層層的深入
找到對應(yīng)的component
最后依然還是調(diào)用activity.viewmodel = component.viewmodel

和普通的dagger對比一下區(qū)別就在于:

Dagger-Android在剛開始的時(shí)候通過注解處理器分析@Component、@Module、@ContributesAndroidInjector 等等注解,幫我們在App啟動(dòng)的時(shí)候建立了一個(gè)全局的單例map,并添加相關(guān)的映射。

可以在生成的DaggerAppComponet.kt文件中找到

private Map<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
    getMapOfClassOfAndProviderOfFactoryOf() {
  return Collections
      .<Class<? extends Activity>, Provider<AndroidInjector.Factory<? extends Activity>>>
          singletonMap(PaoActivity.class, (Provider) paoActivitySubcomponentBuilderProvider);
}

當(dāng)注入的時(shí)候就間接的通過這個(gè)map找到對應(yīng)activity需要的Component,完成注入。

接下來我們就具體來看看 activityInjector.inject(activity)是如何完成注入的。

注入過程

先來斷點(diǎn)調(diào)試一下,看看activityInjector是什么?

activityInjector

)

可以看到activityInjector的真身是DispatchingAndroidInjector,實(shí)際調(diào)用的是DispatchingAndroidInjector的inject()方法,接著看看inject()方法

inject

調(diào)用的是maybeInject(instance),繼續(xù)深入

maybeInject

到這里就很清晰了,正如前文所說的那樣,通過一個(gè)單例map根據(jù)key值為instance.class

找到相應(yīng)的factoryProvider,通過get()方法獲取到AndroidInjector.Factory<T>對象

而它的真身是DaggerAppComponent的內(nèi)部類PaoActivitySubcomponentBuilder。

private final class PaoActivitySubcomponentBuilder
    extends ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder {
    //....
    }

PaoActivitySubcomponentBuilder 繼承了ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder,再來看看ActivityModule_ContributePaoActivity

@Module(subcomponents = ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.class)
public abstract class ActivityModule_ContributePaoActivity {
  private ActivityModule_ContributePaoActivity() {}

  @Binds
  @IntoMap
  @ActivityKey(PaoActivity.class)
  abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
      PaoActivitySubcomponent.Builder builder);

  @Subcomponent
  public interface PaoActivitySubcomponent extends AndroidInjector<PaoActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<PaoActivity> {}
  }
}

這些代碼是添加映射的關(guān)鍵代碼了,Dagger-Android通過處理@ContributesAndroidInjector自動(dòng)生成的,按照Dagger-Android文檔上的說法是可以自己編寫,@ContributesAndroidInjector只是簡化了這步操作。

可以看到@Binds、@IntoMap、@ActivityKey 這幾個(gè)注解,就如前文所說的那樣將其保存到單例的map對象之中,key值便是PaoActivity.class。

這些都是題外話,再回頭繼續(xù)斷點(diǎn)調(diào)試,可以看到factory.create(instance)。

create

調(diào)用了seedInstance()方法,這是一個(gè)抽象方法,由前文的PaoActivitySubcomponentBuilder實(shí)現(xiàn)。

private final class PaoActivitySubcomponentBuilder
      extends ActivityModule_ContributePaoActivity.PaoActivitySubcomponent.Builder {
    private PaoActivity seedInstance;
@Override
public void seedInstance(PaoActivity arg0) {
  this.seedInstance = Preconditions.checkNotNull(arg0);
}
}

就是一個(gè)簡單的賦值操作。然后返回類型為AndroidInjector<T>injector,斷點(diǎn)可以看到它的真身是PaoActivitySubcomponentImpl。

maybeInject

繼續(xù)往下看,來到 injector.inject(instance);

到了這一步,就跟以前的Dagger沒任何區(qū)別了。

 private final class PaoActivitySubcomponentImpl
      implements ActivityModule_ContributePaoActivity.PaoActivitySubcomponent {
    private PaoActivitySubcomponentImpl(PaoActivitySubcomponentBuilder builder) {}

    private PaoRepo getPaoRepo() {
      return new PaoRepo(
          DaggerAppComponent.this.providePaoServiceProvider.get(),
          DaggerAppComponent.this.providePaoDaoProvider.get());
    }

    private PaoViewModel getPaoViewModel() {
      return new PaoViewModel(getPaoRepo());
    }

    @Override
    public void inject(PaoActivity arg0) {
      injectPaoActivity(arg0);
    }

    private PaoActivity injectPaoActivity(PaoActivity instance) {
      PaoActivity_MembersInjector.injectMViewModel(instance, getPaoViewModel());
      return instance;
    }
  }
}

到此,經(jīng)過分析Dagger-Android的注入過程,我們了解了他們的工作原理。
最后總結(jié)歸納一下:

  • 普通的賦值:viewmodel = ViewModel(Repo())
  • Dagger的注入: instance.viewmodel = component.viewmodel
  • Dagger-Android的注入:instance.viewmodel = map.get(instance.class).getComponent().viewmodel

就一個(gè)思路的轉(zhuǎn)變,google的大神真是有心了,搞這么多事。

寫在最后

Dagger-Android相比于普通的Dagger確實(shí)稍微繞了一些,多了一些設(shè)計(jì)模式和面向接口,光看源碼的話很容易繞暈,特別是在不懂得google大神們的思路的時(shí)候。

如果說Dagger的復(fù)雜度是5,那么Dagger-Android的復(fù)雜程度就是7。

如果能明悟的話,邏輯也是很簡單的,然后多進(jìn)行斷點(diǎn)調(diào)試。就像解算法題一樣,Dagger和Dagger-Android可以算是兩種思路吧。

如同寫在前面的話里提到的,Dagger適合于那些初中級開發(fā)者的團(tuán)隊(duì),比較容易理解。Dagger-Android則更適合實(shí)力都較強(qiáng)的開發(fā)團(tuán)隊(duì)。)

github:https://github.com/ditclear/MVVM-Android/tree/dagger-android

參考文檔:
Dagger-Android
告別Dagger2模板代碼:DaggerAndroid原理解析 (推薦)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容