[譯] 全新 Android 注入器:Dagger 2(一)

Dagger 2.10 新增了 Android Support 和 Android Compiler 兩大模塊。對咱們來講,本次改動很是之大,全部 Android 開發者都應儘早嘗試使用這個新的 Android 依賴注入框架。html

在我開始介紹新的 AndroidInjector 類以及 Dagger 2.11 庫以前,若是你對 Dagger 2 還不熟悉甚至以前根本沒用過,那我強烈建議你先去看看 Dagger 入門指南,弄清楚什麼是依賴注入。爲何這麼說呢?由於 Android Dagger 涉及到大量註解,學起來會比較吃力。在我看來,學 Android Dagger 以前你最好先去學學 Dagger 2 和依賴注入。這裏有一篇關於依賴注入的入門文章 Blog 1 以及一篇關於 Dagger 2 的文章 Blog 2前端

老用法

Dagger 2.10 以前,Dagger 2 是這樣用的:java

((MyApplication) getApplication())        
.getAppComponent()        
.myActivity(new MyActivityModule(userId))       
.build()        
.inject(this);
複製代碼

這會有什麼問題呢?咱們想用依賴注入,可是依賴注入的核心原則是什麼?android

一個類不該該關心它是如何被注入的ios

所以咱們必須把這些 Builder 方法和 Module 實例建立部分去掉。git

示例工程

我建立的示例工程中沒作什麼,我想讓它儘量地簡單。它裏面僅包含 MainActivityDetailActivity 兩個 Activity,它們都注入到了相應的 Presenter 實現類而且請求了網絡接口(並非真的發起了 HTTP 請求,我只是寫了一個假方法)。github

準備工做

在 build.gradle 中加入如下依賴:數據庫

compile 'com.google.dagger:dagger:2.11-rc2'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
compile 'com.google.dagger:dagger-android-support:2.11-rc2'
複製代碼

工程包結構

Application 類利用 AppComponent 構建了一張圖譜。AppComponent 類的頭部都被加上 @Component 註解,當 AppComponent 利用它的 Module 進行構建的時候,咱們將獲得一張擁有全部所需實例對象的圖譜。舉個例子,當 App Module 提供了ApiService,咱們在構建擁有 App Module 的 Component 時將會獲得 ApiService 實例對象。後端

若是咱們想將 Activity 加入到 Dagger 圖譜中從而可以直接從父 Compponent 直接獲取所需實例,咱們只需簡單地將 Activity 加上 @Subcomponent 註解便可。在咱們的示例中,DetailActivityComponentMainActivityComponent 類都被加上了 @Subcomponent 註解。最後咱們還有一個必需步驟,咱們須要告訴父 Component 相關的子 Component 信息,所以全部的根 Compponent 都能知道它全部的子 Component。api

先彆着急,我後面會解釋 @Subcomponent@Component 以及 DispatchActivity 都是什麼的。如今只是想讓你對 @Component@Subcomponent 有一個大概瞭解。

@Component and @Component.Builder

**@Component**(modules = {
        AndroidInjectionModule.class,
        AppModule.class,
        ActivityBuilder.class})
public interface AppComponent {

    **@Component.Builder**
    interface Builder {
        **@BindsInstance** _Builder application(Application application);_
        _AppComponent build();_
    }

    void inject(AndroidSampleApp app);
}
複製代碼

**@Component:**Component 是一個圖譜。當咱們構建一個 Component時,Component 將利用 Module 提供被注入的實例對象。

**@Component.Builder:**咱們可能須要綁定一些實例對象到 Component 中,這種狀況咱們能夠經過建立一個帶 @Component.Builder 註解的接口,而後就能夠向 builder 中任意添加咱們想要的方法。在個人示例中,我想將 Application 加入到 AppComponent中。

注意:若是你想爲你的 Component 建立一個 Builder,那你的 Builder 接口中須要有一個返回類型爲你所建立的 Component 的 builder() 方法。

注入 AppComponent

DaggerAppComponent
        ._builder_()
        **.application(this)**
        .build()
        .inject(this);
複製代碼

從上面的代碼能夠看出,咱們將 Application 實例綁定到了 Dagger 圖譜中。

我想你們已經對 @Component.Builder@Component 有了必定的認識,下面我想說說工程的結構。

Component/Module 結構

使用 Dagger 的時候咱們能夠將 App 分爲三層:

  • Application Component
  • Activity Components
  • Fragment Components

Application Component

@Component(modules = {
        AndroidInjectionModule.class,
        AppModule.class,
        ActivityBuilder.class})
public interface AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance Builder application(Application application);
        AppComponent build();
    }

    void inject(AndroidSampleApp app);
}
複製代碼

每一個 Android 應用都有一個 Application 類,這就是爲何我也有一個 Application Component 的緣由。這個 Component 表示是爲應用層面提供實例的 (例如 OkHttp, Database, SharedPrefs)。這個 Component 是 Dagger 圖譜的根,在咱們的應用中 Application Component 提供了三個 Module

  • AndroidInjectionModule:這個類不是咱們寫的,它是 Dagger 2.10 中的一個內部類,經過給定的 Module 爲咱們提供了 Activity 和 Fragment。
  • ActivityBuilder:咱們本身建立的 Module,這個 Module 是給 Dagger 用的,咱們將全部的 Activity 映射都放在了這裏。Dagger 在編譯期間能獲取到全部的 Activity,咱們的 App 中有 MainActivity 和 DetailActivity 兩個 Activity,所以我將這兩個 Activity 都放在這裏。
@Module
public abstract class ActivityBuilder {

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindMainActivity(MainActivityComponent.Builder builder);

    @Binds
    @IntoMap
    @ActivityKey(DetailActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindDetailActivity(DetailActivityComponent.Builder builder);

}
複製代碼
  • AppModule:咱們在這裏提供了 retrofit、okhttp、持久化數據庫、SharedPrefs。其中有一個很重要的細節,咱們必須將子 Component 加入到 AppModule 中,這樣 Dagger 圖譜才能識別。
@Module(subcomponents = {
        MainActivityComponent.class,
        DetailActivityComponent.class})
public class AppModule {

    @Provides
    @Singleton
    Context provideContext(Application application) {
        return application;
    }

}
複製代碼

Activity Components

咱們有兩個 Activity:MainActivity and DetailActivity。它們都擁有本身的 ModuleComponent,可是它們與我在上面 AppModule 中定義的同樣,也是子 Component

  • MainActivityComponent:這個 Component 是鏈接 MainActivityModule 的橋樑,可是有一個很關鍵的不一樣點就是不須要在 Component 中添加 inject() 和 build() 方法。MainActivityComponent 會從父類中集成這些方法。AndroidInjector 類是 dagger-android 框架中新增的。
@Subcomponent(modules = MainActivityModule.class)
public interface MainActivityComponent extends AndroidInjector<MainActivity>{
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity>{}
}
複製代碼
  • MainActivityModule:這個 ModuleMainActivity 提供了相關實例對象(例如 MainActivityPresenter)。你注意到 provideMainView() 方法將 MainActivity 做爲參數了嗎?沒錯,咱們利用 MainActivityComponent 建立了咱們所需的對象。所以 Dagger 將咱們的 Activity 加入到 圖譜中並所以能使用它。
@Module
public class MainActivityModule {

    @Provides
    MainView provideMainView(MainActivity mainActivity){
        return mainActivity;
    }

    @Provides
    MainPresenter provideMainPresenter(MainView mainView, ApiService apiService){
        return new MainPresenterImpl(mainView, apiService);
    }
}
複製代碼

一樣的,咱們能夠像建立 MainActivityComponentMainActivityModule 同樣建立 DetailActivityComponentDetailActivityModule,所以具體步驟就略過了。

Fragment Components

若是在 DetailActivity 中有兩個 Fragment,那咱們應該怎麼辦呢?實際上這一點都不難想到。先想一想 Activity 和 Application 之間的關係,Application 經過映射的 Module(在個人示例中就是ActivityBuilder)知道全部的 Activity,而且將全部的 Activity 做爲子 Component 加入到 AppModule 中。

Activity 和 Fragment 也是如此,首先建立一個 FragmentBuilder Module 加入到 DetailActivityComponent 中。

如今咱們就能夠像以前建立 MainActivityComponentMainActivityModule 同樣來建立 DetailFragmentComponentDetailFragmentModule了。

DispatchingAndroidInjector

最後咱們須要作的即是注入到注入器中。注入器的做用是什麼?我想用一段簡單的代碼解釋下。

public class AndroidSampleApp extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        //simplified
    }

    @Override
    public DispatchingAndroidInjector<Activity> activityInjector() {
        return activityDispatchingAndroidInjector;
    }
}
複製代碼

Application 擁有不少 Activity,這就是咱們實現 HasActivityInjector 接口的緣由。那 Activity 有多個 Fragment 呢?意思是咱們須要在 Activity 中實現 HasFragmentInjector 接口嗎?沒錯,我就是這個意思!

public class DetailActivity extends AppCompatActivity implements HasSupportFragmentInjector {

    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

    //simplified
  
    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
}
複製代碼

若是你沒有子 Fragment 你不須要注入任何東西到 Fragment,那你也不須要實現 HasSupportFragmentInjector 接口了。可是在咱們的示例中須要在 DetailActivity 建立一個 DetailFragment

AndroidInjection.inject(this)

作這些都是爲了什麼?這是由於 Activity 和 Fragment 都不該該是如何被注入的,那咱們應該如何注入呢?

在 Activity 中:

@Override
protected void onCreate(Bundle savedInstanceState) {
 **AndroidInjection._inject_(this);**
    super.onCreate(savedInstanceState);
}
複製代碼

在 Fragment 中:

@Override
public void onAttach(Context context) {
    **AndroidSupportInjection._inject_(this);
   ** super.onAttach(context);
}
複製代碼

沒錯,恭喜你,全部工做都完成了!

我知道這有點複雜,學習曲線很陡峭,可是咱們仍是達到目的了。如今,咱們的類是不知道如何被注入的。咱們能夠將所需實例對象經過 @Inject annotation 註解注入到咱們的 UI 元素。

你能夠在個人 GitHub 主頁找到這個工程,我建議你對照着 Dagger 2 的官方文檔看。

iammert/dagger-android-injection _dagger-android-injection - Sample project explains Dependency Injection in Android using dagger-android framework._github.com

Dagger ‡ A fast dependency injector for Android and Java. A fast dependency injector for Android and Java.google.github.io

在第二部分,我想利用 Dagger 提供的新註解來簡化 android-dagger 注入,可是在簡化以前我想先給你們看看它原來的樣子。

第二部分在這裏了。

感謝閱讀,祝你編碼愉快!


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索