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

来源:http://www.cnblogs.com/tiantianbyconan/archive/2016/12/29/6234811.html
-Advertisement-
Play Games

以下內容為原創,歡迎轉載,轉載請註明 來自天天博客: 使用Dagger 2進行依賴註入 Producers 原文: 本文是在Android中使用Dagger 2框架進行依賴註入的系列文章中的一部分。今天我們將探索下Dagger Producers 使用Java實現非同步依賴註入的Dagger2的一個擴 ...



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

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

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

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

初始化性能問題

我們都知道Dagger 2是一個優化得很好的依賴註入框架。但是即使有這些全部的微優化,仍然在依賴註入的時候存在可能的性能問題 - “笨重”的第三方庫和/或我們那些主線程阻塞的代碼。

依賴註入是在儘可能短的時間內在正確的地方傳遞所請求的依賴的過程 - 這些都是Dagger 2做得很好的。但是DI也會去創建各種依賴。如果我們需要花費幾百毫秒創建它們,那麼以納秒級的時間去提供依賴還有什麼意義呢?

當我們的app創建了一系列繁重的單例並立即由Dagger2提供服務之後也許可能沒有這麼重要。但是在我們創建它們的時候仍然需要一個時間成本 - 大多數情況下決定了app啟動的時間。

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

在很短的時間內,讓我們想象這麼一個場景 - 你的app有一個初始化的界面(SplashScreen),需要在app啟動後立即做一些需要的事情:

  • 初始化所有tracking libs(Goole Analytics, Crashlytics)然後發送第一份數據給它們。
  • 創建用於API和/或資料庫通信的整個棧。
  • 我們試圖的交互邏輯(MVP中的Presenters,MVVM中的ViewModels等等)。

即使我們的代碼是優化地非常好的,但是仍然有可能有些額外的庫需要幾十或者幾百毫秒的時間來初始化。在我們啟動界面之前將展示必須初始化和交付的所有請求的依賴(和它們的依賴)。這意味著啟動時間將會是它們每一個初始化時間的總和。

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

用戶將會在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




您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有沒有覺得瀏覽器自帶的原始滾動條很不美觀,同時也有看到很多網站的自定義滾動條顯得高端,就連chrome32.0開發板都拋棄了原始的滾動條,美觀多了。那webkit瀏覽器是如何自定義滾動條的呢? 前言 webkit支持擁有overflow屬性的區域,列表框,下拉菜單,textarea的滾動條自定義樣式 ...
  • 一、CSS Sprite(雪碧圖|精靈圖)指什麼? 有什麼作用? CSS雪碧 即CSS Sprite,也有人叫它CSS精靈,是一種CSS圖像合併技術,該方法是將小圖像和背景圖片合併到一張圖片上,然後利用css的背景定位來顯示的圖片部分。 作用: 減少載入網頁圖片時對伺服器的請求次數; 提高頁面的載入 ...
  • 一般事件 事件 瀏覽器支持 描述 onClick IE3|N2|O3 滑鼠點擊事件,多用在某個對象控制的範圍內的滑鼠點擊 onDblClick IE4|N4|O 滑鼠雙擊事件 onMouseDown IE4|N4|O 滑鼠上的按鈕被按下了 onMouseUp IE4|N4|O 滑鼠按下後,鬆開時激發 ...
  • 小程式裡面的轉換時間格式代碼 其實我最看重的是 ...
  • 屬於轉載 http status code:200:成功,伺服器已成功處理了請求,通常這表示伺服器提供了請求的網頁 404:未找到,伺服器未找到 201-206都表示伺服器成功處理了請求的狀態代碼,說明網頁可以正常訪問。 200(成功) 伺服器已成功處理了請求。通常,這表示伺服器提供了請求的網頁。 ...
  • 現在的css3真是強大,之前很多動畫都是用jq來實現,但是css3製作的動畫要比jq實現起來簡單很多,今天呢,我自己也寫了一個css旋轉動畫和大家分享。效果如下麵的圖片 思路:1.製作之前呢,我們先來整理一下思路,這個圓軸軌跡上一共有八個圓。 仔細看的話,你會發現一個規律,那就是一個月是360度的, ...
  • 前言 昨日,我請了一天假去考科目三,結果第一把掛在了沒完全關閉燈光上,第二把掛在轉彎時沒有觀察後方車輛,當聽到師傅一句“下去”的時候,我那是悲痛的面紅耳赤,這讓我很鬱悶,晚上也就不想回去上班了,回家後仍然有點低沉,在這種情況下,不寫點毒雞湯,好像已經不能好好的調節心情了,看看時間年底了,便寫寫今年的 ...
  • 學習 Activity 生命周期時希望通過 Dialog 主題測試 onPause() 和 onStop() 的區別。 點擊按鈕跳轉 Activity 時報錯: 找到最有用的一句話,說明所使用的 theme 和當前 Activity 不匹配。 AndroidManifest.xml 中 activi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...