文檔類(lèi)型:翻譯
原文鏈接 : https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3
譯者注:轉(zhuǎn)載請(qǐng)注明原文及譯文出處,本文僅供學(xué)術(shù)交流,不作任何商業(yè)使用,如有侵權(quán),請(qǐng)聯(lián)系刪除。
Dagger版本:2.16
Dagger 2.10版本引入的dagger-android是一個(gè)專(zhuān)為Android設(shè)計(jì)的除了Dagger主模塊和dagger-compiler之外的全新的模塊,本文中,我們將介紹使用dagger-android的方法步驟,當(dāng)然,前提是你要具備相關(guān)的Dagger知識(shí)。
本文主要介紹Activity的注入,但是也可以作為其他Android組件注入的參考
Dagger常用配置
在Android端的Dagger的常用配置包含包含Application Component 以及Application Module,前者是用來(lái)注入Activity,F(xiàn)ragment等Android組件。
Application Component 代碼
@Component(modules = { AppModule.class })
interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance Builder application(App application);
AppComponent build();
}
void inject(FeatureActivity featureActivity);
}
Application Module 代碼
@Module
abstract class AppModule {
@Provides
static Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton
@Provides
static SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
App的Application 代碼
@Module
abstract class AppModule {
@Provides
static Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton
@Provides
static SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
因?yàn)锳ndroid框架已經(jīng)將這些組件實(shí)例化,所以我們必須執(zhí)行成員注入,使用 @Inject注解對(duì)可見(jiàn)類(lèi)進(jìn)行標(biāo)注,如下:
public class FeatureActivity extends AppCompatActivity {
@Inject SomeClientApi mSomeClientApi;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((App)getApplication())
.getAppComponent()
.inject(this);
}
}
然而,這種方式破壞了依賴(lài)注入的核心準(zhǔn)則:一個(gè)類(lèi)不應(yīng)該知道它是如何被注入的,最新引入的dagger-android模塊就是為了解決這個(gè)問(wèn)題——將注入類(lèi)與注入器分離開(kāi)來(lái)。
使用新的dagger-android模塊
首先,將以下Gradle的依賴(lài)添加到你的build.gradle文件中:
// Dagger core dependencies
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
implementation 'com.google.dagger:dagger:2.16'
// Dagger Android dependencies
annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
implementation 'com.google.dagger:dagger-android:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16'
在我們的項(xiàng)目中,新模塊的配置需要不止一個(gè)component和module。每一個(gè)Activity都需要自己的subComponent并將其與Application的Component連接起來(lái)。以下是我建議的類(lèi)結(jié)構(gòu):
/
| App (extending Application)
| AppComponent
| AppModule
| ContributeAndroidInjectorsModule // 3
+ feature/
| FeatureModule // 2
| FeatureActivityModule // 1
| FeatureActivity
正如我們所見(jiàn),在上文中所提到的典型配置之外額外增加了三個(gè)類(lèi)。每一個(gè)feature擁有它自己的component以及module。
注:我使用術(shù)語(yǔ)“feature”來(lái)描述一個(gè)app中的顯示界面(或者說(shuō)一個(gè)Activity)。
1.FeatureActivityModule
正如前文提到的,每一個(gè)Activity現(xiàn)在需要它自己的subcomponent,為此,Dagger引入了一個(gè)非常方便的注解來(lái)供我們使用以提醒Dagger生成subcomponent的代碼。就像這樣:
@Module
public abstract class FeatureActivityModule {
@ActivityScope
@ContributesAndroidInjector(modules = { FeatureModule.class })
abstract FeatureActivity contributeFeatureActivityInjector();
}
2.FeatureModule
我們將Activity自身需要的綁配置在這個(gè)module中。此module中的綁定僅僅在此Activity和它的subcomponent中有效,除非該module在其他component中使用。假設(shè)我們正在使用MVP的設(shè)計(jì)模式,一下是你通常用來(lái)綁定View的方式:
@Module
abstract class FeatureModule {
@ActivityScope
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
}
3.ContributeActivityModule
到目前為止,我們告訴了Dagger為我們的Activity生成subcomponent,但是我們并沒(méi)有將subcomponent與Applicaiton的Component連接起來(lái)。為了實(shí)現(xiàn)這個(gè),我們需要另一個(gè)module來(lái)持有所有Activity的module。這個(gè)module將會(huì)被包含在Application的Component中。
@Module(includes = {
FeatureActivityModule.class
// Other Activity modules will be added here
})
abstract class ContributeActivityModule {
}
4.AppComponent
Applicaiton的Component配置(使用Kotlin)
我更喜歡使用Java來(lái)寫(xiě)Dagger的module,因?yàn)镵otlin缺少static修飾符,所以當(dāng)我們使用companion object時(shí)需要在同一個(gè)類(lèi)中增加額外的
@Module注解,這是我不太喜歡的。
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class, // 1
ContributeActivityModule::class, // 2
AppModule::class
])
interface AppComponent : AndroidInjector<App> { // 3
@Component.Builder
abstract class Builder : AndroidInjector.Builder<App>() // 4
}
(譯者注:Java代碼如下)
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class, // 1
ContributeActivityModule::class, // 2
AppModule::class
])
public interface AppComponent extends AndroidInjector<App> { // 3
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<App> {
}
}
注意:AppComponent中的一些修改:
- 為了使Dagger Android能夠正常運(yùn)行,我們?cè)贏pplication的Component中包含了AndroidSupportInjectionModule
- 包含ContributeActivityModule是為了將Activity的subcomponent與AppComponent連接起來(lái)
- AppComponent必須繼承自AndroidInjector 并將其泛型設(shè)定為
Application類(lèi) - Appcomponent的Builder可以選擇性的繼承AndroidInjector.Builder并提供Application類(lèi)。因?yàn)檫@個(gè)base類(lèi)已經(jīng)實(shí)現(xiàn)了公共的component builder,這個(gè)builder中包含了一個(gè)設(shè)置Application實(shí)例的方法以及另一個(gè)構(gòu)建component的方法,這可以很輕松的為我們介紹一些代碼行數(shù)。
5.然后我們修改我們的App文件,使其繼承DaggerApplication并實(shí)現(xiàn)基本方法,代碼如下
class App : DaggerApplication() {
private val appComponent: AndroidInjector<App> by lazy {
DaggerAppComponent
.builder()
.create(this)
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return appComponent
}
}
(譯者注:Java代碼如下)
public class App extends DaggerApplication {
private AppComponent appCompoent;
@Override
public void onCreate() {
appCompoent = DaggerAppComponent.builder()
.application(this)
.build();
super.onCreate();
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return appCompoent;
}
}
6.最后,在FeatureActivity中,我們將之前寫(xiě)的注入Activity的代碼刪除,同時(shí)使其繼承自DaggerAppCompatActivity而不是AppCompatActivity
class FeatureActivity : DaggerAppCompatActivity(), FeatureView {
@Inject
internal lateinit var presenter: FeaturePresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.feature_activity)
}
}
(譯者注:Java代碼如下)
public class FeatureActivity extends DaggerAppCompatActivity implements FeatureView {
public static final String EXTRA_SOME_ID = "some_id";
@Inject FeaturePresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.feature_activity);
}
}
向Acivity的Component中注入自定義參數(shù)
在某些場(chǎng)景下你希望注入某些由Activity自己提供的參數(shù)。之前常用的方法是在調(diào)用Activity的component的inject()法法之前,將參數(shù)傳遞給module的構(gòu)造函數(shù)。例如,如果我們遵循MVP的設(shè)計(jì)模式,我們餓Presenter將會(huì)把View作為其構(gòu)造參數(shù)的一部分,這意味著我們需要將Activity作為一個(gè)參數(shù)傳遞給module的構(gòu)造函數(shù)。在使用dagger-android之前,寫(xiě)法是這樣:
@Module
class FeatureModule {
private FeatureView view;
public FeatureModule(FeatureView view) {
this.view = view;
}
@Provides FeatureView provideView() {
return view;
}
}
同時(shí),在Presenter中我們會(huì)使用構(gòu)造函數(shù)注入:
class FeaturePresenter {
private final FeatureView view;
@Inject
Presenter(FeatureView view) {
this.view = view;
}
public void doSomething() {
}
}
最后,構(gòu)建Component,傳入一個(gè)mudle的新實(shí)例,并注入Activity:
class FeaturePresenter {
private final FeatureView view;
@Inject
Presenter(FeatureView view) {
this.view = view;
}
public void doSomething() {
}
}
但是,我們?nèi)绾问褂眯碌?em>dagger-android模塊來(lái)實(shí)現(xiàn)這個(gè)呢?畢竟,我們不再需要手動(dòng)注入Activity,因此無(wú)法像以前那樣接觸到module的創(chuàng)建。
答案是,我們不再需要這樣做了。使用dagger-android模塊,Activity已經(jīng)是圖表的一部分了。這到底是什么意思呢?你應(yīng)該可以記得,我們?cè)?code>FeatureModule做了一下配置,用于在任何使用FeatureView的地方,綁定使用FeatureActivity:
@Module
public abstract class FeatureModule {
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
}
}
但是,假如我們想通過(guò)接受Activity的Intent向Presenter傳遞參數(shù)呢?假設(shè)你通過(guò)Activity的Intent的extra傳遞了一個(gè)唯一的ID,并且Presenter需要這個(gè)ID,例如,Presenter需要這個(gè)ID來(lái)進(jìn)行HTTP請(qǐng)求,實(shí)現(xiàn)這個(gè)的方式就是使用@Named 注解作為限定符,因此我們的Presenter的代碼如下:
class FeaturePresenter {
private FeatureView featureView;
private String someId;
@Inject
public FeaturePresenter(FeatureView featureView, @Named("someId") String someId) {
this.featureView = featureView;
this.someId = someId;
}
public void foo() {
featureView.showFoo();
}
}
答案是非常簡(jiǎn)單的,我們給FretureModule增加了一個(gè)Provides方法,它將會(huì)持有一個(gè)FeatureActivity的實(shí)例,并從intent的extras中獲取我們需要的信息:
@Module
abstract class FeatureModule {
@ActivityScope
@Binds
abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
@ActivityScope
@Provides
@Named("someId")
static String provideSomeId(FeatureActivity featureActivity) {
return featureActivity.getIntent().getStringExtra(FeatureActivity.EXTRA_SOME_ID);
}
}
如前所述,這因?yàn)?code>FeatureActivity已經(jīng)在圖標(biāo)中,所以這種實(shí)現(xiàn)成為可能。
除了Activity之外的注入
如本文開(kāi)頭所提到的,F(xiàn)ragment注入和其他Android組件都不在本文的范圍之內(nèi)。官方文檔介紹了注入Fragment對(duì)象,我強(qiáng)烈建議您閱讀它。還有更多關(guān)于Services、Receivers 和ContentProviders的信息。
總結(jié)
Dagger-android模塊非常接近Android的依賴(lài)注入。如果你正在開(kāi)發(fā)一個(gè)新的項(xiàng)目并且準(zhǔn)備使用Dagger,你一定要使用Dagger-Android的配置。
官方的示例對(duì)我來(lái)說(shuō)太簡(jiǎn)單了,所以你可以看看我自己的Demo。