[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

相关文章
相关标签/搜索