Dagger-Android使用手冊(cè)

文檔類(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。

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

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