[Android]使用Dagger 2进行依赖注入 - Producers(翻译)
使用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 2compile '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
public class GithubApiProducerModule {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();}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();}public GithubApiService produceGithubApiService(Retrofit restAdapter) {return restAdapter.create(GithubApiService.class);}public UserManager produceUserManager(GithubApiService githubApiService) {return new UserManager(githubApiService);}public UserModule.Factory produceUserModuleFactory(GithubApiService githubApiService) {return new UserModule.Factory(githubApiService);} }看起來很像?沒錯,我們只是修改了:
- @Module?改為?@ProducerModule
- @Provides @Singleton?改為?@Produces。你還記得嗎?在Producers中我們默認就有一個單例
UserModule.Factory?依賴只是因為app的邏輯原因而添加。
Production Component
現在讓我們創建@ProductionComponent,它將會為UserManager實例提供服務:
(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>() {public void onSuccess(UserManager result) {SplashActivityPresenter.this.userManager = result;}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
本文轉自天天_byconan博客園博客,原文鏈接:
http://www.cnblogs.com/tiantianbyconan/p/6234811.html,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的[Android]使用Dagger 2进行依赖注入 - Producers(翻译)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度地图实现定位功能
- 下一篇: Android 权限的一些细节