- 原文地址:New Android Injector with Dagger 2—part 1
- 原文作者:Mert Şimşek
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:MummyDing
- 校對者:LeviDing
Dagger 2.10 新增了 Android Support 和 Android Compiler 兩大模組。對我們來說,本次改動非常之大,所有 Android 開發者都應儘早嘗試使用這個新的 Android 依賴注入框架。
在我開始介紹新的 AndroidInjector 類以及 Dagger 2.11 庫之前,如果你對 Dagger 2 還不熟悉甚至之前根本沒用過,那我強烈建議你先去看看 Dagger 入門指南,弄清楚什麼是依賴注入。為什麼這麼說呢?因為 Android Dagger 涉及到大量註解,學起來會比較吃力。在我看來,學 Android Dagger 之前你最好先去學學 Dagger 2 和依賴注入。這裡有一篇關於依賴注入的入門文章 Blog 1 以及一篇關於 Dagger 2 的文章 Blog 2。
老用法
Dagger 2.10 之前,Dagger 2 是這樣用的:
((MyApplication) getApplication())
.getAppComponent()
.myActivity(new MyActivityModule(userId))
.build()
.inject(this);
複製程式碼
這會有什麼問題呢?我們想用依賴注入,但是依賴注入的核心原則是什麼?
一個類不應該關心它是如何被注入的
因此我們必須把這些 Builder 方法和 Module 例項建立部分去掉。
示例工程
我建立的示例工程中沒做什麼,我想讓它儘可能地簡單。它裡面僅包含 MainActivity
和 DetailActivity
兩個 Activity,它們都注入到了相應的 Presenter 實現類並且請求了網路介面(並不是真的發起了 HTTP 請求,我只是寫了一個假方法)。
準備工作
在 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 註解即可。在我們的示例中,DetailActivityComponent
和 MainActivityComponent
類都被加上了 @Subcomponent 註解。最後我們還有一個必需步驟,我們需要告訴父 Component 相關的子 Component 資訊,因此所有的根 Compponent 都能知道它所有的子 Component。
先彆著急,我後面會解釋 @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
。它們都擁有自己的 Module 和 Component,但是它們與我在上面 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:這個 Module 為
MainActivity
提供了相關例項物件(例如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);
}
}
複製程式碼
同樣的,我們可以像建立 MainActivityComponent
和 MainActivityModule
一樣建立 DetailActivityComponent
和 DetailActivityModule
,因此具體步驟就略過了。
Fragment Components
如果在 DetailActivity
中有兩個 Fragment,那我們應該怎麼辦呢?實際上這一點都不難想到。先想想 Activity 和 Application 之間的關係,Application 通過對映的 Module(在我的示例中就是ActivityBuilder)知道所有的 Activity,並且將所有的 Activity 作為子 Component 加入到 AppModule 中。
Activity 和 Fragment 也是如此,首先建立一個 FragmentBuilder Module 加入到 DetailActivityComponent 中。
現在我們就可以像之前建立 MainActivityComponent
和 MainActivityModule
一樣來建立 DetailFragmentComponent
和 DetailFragmentModule
了。
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 的官方文件看。
在第二部分,我想利用 Dagger 提供的新註解來簡化 android-dagger 注入,但是在簡化之前我想先給大家看看它原來的樣子。
第二部分在這裡了。
感謝閱讀,祝你編碼愉快!
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。