[Android]使用Dagger 2進行依賴注入 - Producers(翻譯)


如下內容爲原創,歡迎轉載,轉載請註明
來自每天博客:http://www.cnblogs.com/tiantianbyconan/p/6234811.html
html

使用Dagger 2進行依賴注入 - Producers

原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-producers/java

本文是在Android中使用Dagger 2框架進行依賴注入的系列文章中的一部分。今天咱們將探索下Dagger Producers - 使用Java實現異步依賴注入的Dagger2的一個擴展。android

初始化性能問題

咱們都知道Dagger 2是一個優化得很好的依賴注入框架。可是即便有這些所有的微優化,仍然在依賴注入的時候存在可能的性能問題 - 「笨重」的第三方庫和/或咱們那些主線程阻塞的代碼。git

依賴注入是在儘量短的時間內在正確的地方傳遞所請求的依賴的過程 - 這些都是Dagger 2作得很好的。可是DI也會去建立各類依賴。若是咱們須要花費幾百毫秒建立它們,那麼以納秒級的時間去提供依賴還有什麼意義呢?github

當咱們的app建立了一系列繁重的單例並當即由Dagger2提供服務以後也許可能沒有這麼重要。可是在咱們建立它們的時候仍然須要一個時間成本 - 大多數狀況下決定了app啓動的時間。數據庫

這問題(已經給了提示怎麼去調適它)已經在我以前的一篇博客中描述地很詳細了:Dagger 2 - graph creation performanceapi

在很短的時間內,讓咱們想象這麼一個場景 - 你的app有一個初始化的界面(SplashScreen),須要在app啓動後當即作一些須要的事情:緩存

  • 初始化全部tracking libs(Goole Analytics, Crashlytics)而後發送第一份數據給它們。
  • 建立用於API和/或數據庫通訊的整個棧。
  • 咱們試圖的交互邏輯(MVP中的Presenters,MVVM中的ViewModels等等)。

即便咱們的代碼是優化地很是好的,可是仍然有可能有些額外的庫須要幾十或者幾百毫秒的時間來初始化。在咱們啓動界面以前將展現必須初始化和交付的全部請求的依賴(和它們的依賴)。這意味着啓動時間將會是它們每個初始化時間的總和。多線程

AndroidDevMetrics 測量的示例堆棧可能以下所示:app

用戶將會在600ms(+額外的系統work)內看到SplashActivity - 全部初始化時間的總和。

Producers - 異步依賴注入

Dagger 2 有一個名爲 Producers 的擴展,或多或少能爲咱們解決這些問題。

思路很簡單 - 整個初始化流程能夠在一個或多個後臺線程中被執行,而後延後再交付給app的主線程。

@ProducerModule

相似於@Module,這個被用來標記用於傳遞依賴的類。多虧於它,Dagger將會知道去哪裏找到被請求的依賴。

@Produces

相似於@Provide,這個註解用來標記帶有@ProducerModule註解的類中的返回依賴的方法。@Produces註解的方法能夠返回ListenableFuture<T>或者自身的對象(也會在所給的後臺線程中進行初始化)。

@ProductionComponent

相似於@Component,它負責依賴的傳遞。它是咱們代碼與@ProducerModule之間的橋樑。惟一跟@Component的不一樣之處是咱們不能決定依賴的scope。這意味着提供給 component 的每個 Produces 方法每一個component 實例 中最多隻會被調用一次,無論它做爲一個 依賴 用於多少次綁定。

也就是說,每個服務於@ProductionComponent的對象都是一個單例(只要咱們從這個特殊的component中獲取)。


Producers的文檔已經足夠詳細了,因此這裏沒有必要去拷貝到這裏。直接看:Dagger 2 Producers docs

Producers的代價

在咱們開始實踐前,有一些值得提醒的事情。Producers相比Dagger 2自己有一點更復雜。它看起來手機端app不是他們它們主要使用的目標,並且知道這些事情很重要:

  • Producers使用了Guava庫,而且創建在ListenableFuture類之上。這意味着你不得不處理15k的額外方法在你的app中。這可能致使你不得不使用Proguard來處理而且須要一個更長的編譯時間。
  • 就如你將看到的,建立ListenableFutures並非沒有成本的。因此若是你期望Producers會幫你從10ms優化到0ms那你可能就錯了。可是若是規模更大(100ms --> 10ms),你就能有所發現。
  • 如今沒法使用@Inject註解,因此你必需要手動處理ProductionComponents。它會使得你的標準整潔的代碼變得混亂。

這裏你能夠針對@Inject註解找到好的間接的解決方案的嘗試。

Example app

若是你仍然但願使用Producers來處理,那就讓咱們更新 GithubClient 這個app使得它在注入過程使用Producers。在實現以前和以後咱們將會使用 AndroidDevMetrics 來測量啓動時間和對比結果。

這裏是一個在使用producers更新以前的 GithubClient app的版本。而且它測量的平均啓動時間以下:

咱們的計劃是處理UserManager讓它的全部的依賴來自Producers。

配置

咱們將給一個Dagger v2.1的嘗試(可是當前2.0版本的Producers也是可用的)。

讓咱們在項目中加入一個Dagger新的版本:

app/build.gradle:

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'com.frogermcs.androiddevmetrics'

repositories {
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots"
    }
}
//...

dependencies {
    //...

    //Dagger 2
    compile 'com.google.dagger:dagger:2.1-SNAPSHOT'
    compile 'com.google.dagger:dagger-producers:2.1-SNAPSHOT'
    apt 'com.google.dagger:dagger-compiler:2.1-SNAPSHOT'

    //...
}

如你所見,Producers 做爲一個新的依賴,在dagger 2庫的下面。還有值得一說的是Dagger v2.1終於不須要org.glassfish:javax.annotation:10.0-b28的依賴了。

Producer Module

如今,讓咱們移動代碼從GithubApiModule到新建立的GithubApiProducerModule中。原來的代碼能夠在這裏找到:GithubApiModule

GithubApiProducerModule.java

@ProducerModule
public class GithubApiProducerModule {

    @Produces
    static OkHttpClient produceOkHttpClient() {
        final OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (BuildConfig.DEBUG) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(logging);
        }

        builder.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
                .readTimeout(60 * 1000, TimeUnit.MILLISECONDS);

        return builder.build();
    }

    @Produces
    public Retrofit produceRestAdapter(Application application, OkHttpClient okHttpClient) {
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.client(okHttpClient)
                .baseUrl(application.getString(R.string.endpoint))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create());
        return builder.build();
    }

    @Produces
    public GithubApiService produceGithubApiService(Retrofit restAdapter) {
        return restAdapter.create(GithubApiService.class);
    }

    @Produces
    public UserManager produceUserManager(GithubApiService githubApiService) {
        return new UserManager(githubApiService);
    }

    @Produces
    public UserModule.Factory produceUserModuleFactory(GithubApiService githubApiService) {
        return new UserModule.Factory(githubApiService);
    }
}

看起來很像?沒錯,咱們只是修改了:

  • @Module 改成 @ProducerModule
  • @Provides @Singleton 改成 @Produces你還記得嗎?在Producers中咱們默認就有一個單例

UserModule.Factory 依賴只是由於app的邏輯緣由而添加。

Production Component

如今讓咱們建立@ProductionComponent,它將會爲UserManager實例提供服務:

@ProductionComponent(
        dependencies = AppComponent.class,
        modules = GithubApiProducerModule.class
)
public interface AppProductionComponent {
    ListenableFuture<UserManager> userManager();

    ListenableFuture<UserModule.Factory> userModuleFactory();
}

又一次,很是相似原來的Dagger's @Component

ProductionComponent的構建也是與標準的Component很是類似:

AppProductionComponent appProductionComponent = DaggerAppProductionComponent.builder()
    .executor(Executors.newSingleThreadExecutor())
    .appComponent(appComponent)
    .build();

額外附加的參數是Executor實例,它告訴ProductionComponent依賴應該在哪裏(哪一個線程)被建立。在咱們的例子中咱們使用了一個single-thread executor,可是固然增長並行級別並使用多線程執行不是一個問題。

獲取依賴

就像我說的,當前咱們不能去使用@Inject註解。相反,咱們必須直接詢問ProductionComponent(你能夠在SplashActivityPresenter找到這些代碼):

appProductionComponent = splashActivity.getAppProductionComponent();
Futures.addCallback(appProductionComponent.userManager(), new FutureCallback<UserManager>() {
    @Override
    public void onSuccess(UserManager result) {
        SplashActivityPresenter.this.userManager = result;
    }

    @Override
    public void onFailure(Throwable t) {

    }
});

這裏重要的是,對象初始化是在你第一次調用appProductionComponent.userManager()的時候開始的。在這以後UserManager對象將會被緩存。這表示每個綁定都擁有跟component實例相同的生命週期。

以上幾乎就是全部了。固然你應該知道在Future.onSuccess()方法被調用以前userManager實例會時null

性能

在最後讓咱們來看下如今注入的性能是怎麼樣的:

是的,沒錯 - 這時平均值大約是15ms。它小於同步注入(平均. 25ms)可是並不如你指望的那樣少。這時由於Producers並不像Dagger自己那樣輕量。

因此如今取決於你了 - 是否值得使用Guava, Proguard和代碼複雜度來作這種優化。

請記住,若是你以爲Producers並非最適合你的app的,你能夠在你的app中嘗試使用RxJava或者其餘異步代碼來包裝你的注入。

感謝閱讀!

代碼:

以上描述的完整代碼可見Github repository

做者

Miroslaw Stanek

Head of Mobile Development @ Azimo


[Android]使用Dagger 2依賴注入 - DI介紹(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/5092083.html


[Android]使用Dagger 2依賴注入 - API(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/5092525.html


[Android]使用Dagger 2依賴注入 - 自定義Scope(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/5095426.html


[Android]使用Dagger 2依賴注入 - 圖表建立的性能(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/5098943.html


[Android]Dagger2Metrics - 測量DI圖表初始化的性能(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/5193437.html


[Android]使用Dagger 2進行依賴注入 - Producers(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/6234811.html


[Android]在Dagger 2中使用RxJava來進行異步注入(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/6236646.html


[Android]使用Dagger 2來構建UserScope(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/6237731.html


[Android]在Dagger 2中Activities和Subcomponents的多綁定(翻譯):

http://www.cnblogs.com/tiantianbyconan/p/6266442.html

相關文章
相關標籤/搜索