以下内容为原创,欢迎转载,转载请注明
来自天天博客:

# 在Dagger 2中使用RxJava来进行异步注入

> 原文:

几星期前我写了一篇关于在Dagger 2中使用*Producers*进行异步注入的[文章](https://medium.com/@froger_mcs/dependency-injection-with-dagger-2-producers-c424ddc60ba3)。在后台线程中执行对象的初始化又一个很好的优势 - 它负责实时([每秒60帧](https://www.youtube.com/watch?v=CaMTIgxCSqU)可以保持界面流畅)绘制UI时不会在主线程中阻塞。

值得一提的是,缓慢的初始化过程并不是每个人都会觉得是个问题。但是如果你真的关心这个,所有外部库在构造以及在任何`init()`方法中进行磁盘/网络的操作会很常见。如果你不能确定这一点,我建议你尝试下[AndroidDevMetrics](https://github.com/frogermcs/AndroidDevMetrics) - 我的Android性能测量库。它会告诉你在app中需要花多少时间来显示特定的界面,还有(如果你使用了Dagger 2)在依赖图表中提供每个对象消耗了多少时间。

不幸的是Producers并不是为Android设计的,它有以下缺陷:

- 依赖使用了Guava(会引起64k方法问题,增加build时间)
- 并不是非常快的(注入机制会阻塞主线程几毫秒到几十毫秒的世界,这取决于设备)
- 不能使用@Inject注解(代码会有一点混乱)

虽然我们不能解决最后两个问题,但是第一个我们可以在Android Project中解决。

## 使用RxJava进行异步注入

幸运的是,有大量的Android开发者使用了RxJava(和[RxAndroid](https://github.com/ReactiveX/RxAndroid))来在我们app中编写异步代码。让我们来尝试在Dagger 2中使用它来进行异步注入。

### 异步@Singleton注入

这是我们繁重的对象:

```java
@Provides
@Singleton
HeavyExternalLibrary provideHeavyExternalLibrary() {
HeavyExternalLibrary heavyExternalLibrary = new HeavyExternalLibrary();
heavyExternalLibrary.init(); //This method takes about 500ms
return heavyExternalLibrary;
}
```

现在让我们来创建一个额外的`provide...()`方法,它返回一个`Observable`对象,它会异步调用以下代码:

```java
@Singleton
@Provides
Observable provideHeavyExternalLibraryObservable(final Lazy heavyExternalLibraryLazy) {
return Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber subscriber) {
subscriber.onNext(heavyExternalLibraryLazy.get());
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
```

让我们逐行来分析:

- `@Singleton` - 记住这个很重要,`Observable`对象将会是一个单例,而不是`HeavyExternalLibrary`。Singleton也会阻止创建额外的Observable对象。
- `@Providers` - 因为这个方法是`@Module`注解了的类的一部分。你还记得[Dagger 2 API](http://frogermcs.github.io/dependency-injection-with-dagger-2-the-api/)吗?
- `Lazy heavyExternalLibraryLazy`对象阻止Dagger(否则,在调用`provideHeavyExternalLibraryObservable()`方法调用的瞬间对象就会被创建)内部对HeavyExternalLibrary对象的初始化。
- `Observable.create(...)`代码 - 它将在每次这个Observable被订阅时通过调用`heavyExternalLibraryLazy.get()`返回`heavyExternalLibrary`对象。
- `.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());` - 默认情况下RxJava代码会在Observable被创建的线程中执行。这就是为什么我们要把执行移动到后台线程(这里的`Schedulers.io()`),然后在主线程中(`AndroidSchedulers.mainThread()`)观察结果。

我们的Observable像图表中其它对象一样被注入,但是`heavyExternalLibrary`对象本身将会延迟一点才可用:

```java
public class SplashActivity {

@Inject
Observable heavyExternalLibraryObservable;

//This will be injected asynchronously
HeavyExternalLibrary heavyExternalLibrary;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate();
//...
heavyExternalLibraryObservable.subscribe(new SimpleObserver() {
@Override
public void onNext(HeavyExternalLibrary heavyExternalLibrary) {
//Our dependency will be available from this moment
SplashActivity.this.heavyExternalLibrary = heavyExternalLibrary;
}
});
}
}
```

### 异步新实例的注入

上面的代码展示了怎么去注入单例的对象。那如果我们想异步注入新的实例呢?

确认我们的对象不再使用了@Singleton注解:

```java
@Provides
HeavyExternalLibrary provideHeavyExternalLibrary() {
HeavyExternalLibrary heavyExternalLibrary = new HeavyExternalLibrary();
heavyExternalLibrary.init(); //This method takes about 500ms
return heavyExternalLibrary;
}
```

我们`Observable` provider方法也会有一点改变。我们不能使用`Lazy`因为它只会在第一次调用`get()`方法的时候(详见[Lazy文档](http://google.github.io/dagger/api/latest/dagger/Lazy.html))才会创建新的实例。

这里是更新后的代码:

```java
@Singleton
@Provides
Observable provideHeavyExternalLibraryObservable(final Provider heavyExternalLibraryProvider) {
return Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber subscriber) {
subscriber.onNext(heavyExternalLibraryProvider.get());
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
```

我们的`Observable`可以是一个单例,,但是每一次我们去调用它的subscribe()方法的时候,我们将会在onNext()方法中得到一个新的`HeavyExternalLibrary`实例:

```java
heavyExternalLibraryObservable.subscribe(new SimpleObserver() {
@Override
public void onNext(HeavyExternalLibrary heavyExternalLibrary) {
//New instance of HeavyExternalLibrary
}
});
```

### 完全的异步注入

还有另一个方法是用RxJava在Dagger 2中进行异步注入。我们可以使用Observable简单封装整个注入过程。

我们注入的执行是这样的(代码摘自[GithubClient](https://github.com/frogermcs/GithubClient/)项目):

```java
public class SplashActivity extends BaseActivity {

@Inject
SplashActivityPresenter presenter;
@Inject
AnalyticsManager analyticsManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

//This method is called in super.onCreate() method
@Override
protected void setupActivityComponent() {
final SplashActivityComponent splashActivityComponent = GithubClientApplication.get(SplashActivity.this)
.getAppComponent()
.plus(new SplashActivityModule(SplashActivity.this));
splashActivityComponent.inject(SplashActivity.this);
}
}
```

要让它变成异步我们只需要使用Observable封装`setupActivityComponent()`方法:

```java
@Override
protected void setupActivityComponent() {
Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber subscriber) {
final SplashActivityComponent splashActivityComponent = GithubClientApplication.get(SplashActivity.this)
.getAppComponent()
.plus(new SplashActivityModule(SplashActivity.this));
splashActivityComponent.inject(SplashActivity.this);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SimpleObserver() {
@Override
public void onCompleted() {
//Here is the moment when injection is done.
analyticsManager.logScreenView(getClass().getName());
presenter.callAnyMethod();
}
});
}
```

正如注释,所有`@Inject`注解了的对象将被未来某一时刻注入。在返回注入过程是异步的并且不会对主线程有很大的影响。

当然创建`Observable`对象和额外`subscribeOn()`线程并不是完全免费的 - 它将会花费一点时间。这类似于**Producers**代码所产生的影响。

感谢阅读!

## 作者

[Miroslaw Stanek](http://about.me/froger_mcs)

Head of Mobile Development @ [Azimo](https://azimo.com/)

> __[Android]使用Dagger 2依赖注入 - DI介绍(翻译):__

> __[Android]使用Dagger 2依赖注入 - API(翻译):__

> __[Android]使用Dagger 2依赖注入 - 自定义Scope(翻译):__

> __[Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译):__

> __[Android]Dagger2Metrics - 测量DI图表初始化的性能(翻译):__

> __[Android]使用Dagger 2进行依赖注入 - Producers(翻译):__

> __[Android]在Dagger 2中使用RxJava来进行异步注入(翻译):__

> __[Android]使用Dagger 2来构建UserScope(翻译):__

[Android]在Dagger 2中使用RxJava来进行异步注入(翻译)的更多相关文章

  1. [Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6266442.html 在Dagger 2中Activities ...

  2. Android 和 Dagger 2 中的依赖注入

    原文:Dependency Injection in Android with Dagger 2 作者:Joe Howard 译者:kmyhy 在现代开发团队中到处充斥着"你一定要用依赖注入 ...

  3. [Android]使用Dagger 2来构建UserScope(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6237731.html 使用Dagger 2来构建UserSco ...

  4. [Android]使用Dagger 2进行依赖注入 - Producers(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6234811.html 使用Dagger 2进行依赖注入 - P ...

  5. [Android]使用Dagger 2依赖注入 - DI介绍(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092083.html 使用Dagger 2依赖注入 - DI介 ...

  6. [Android]使用Dagger 2依赖注入 - API(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092525.html 使用Dagger 2依赖注入 - API ...

  7. [Android]使用Dagger 2依赖注入 - 自定义Scope(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5095426.html 使用Dagger 2依赖注入 - 自定义 ...

  8. [Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5098943.html 使用Dagger 2依赖注入 - 图表创 ...

  9. [Android] 怎么在应用中实现密码隐藏?

    [Android] 怎么在应用中实现密码隐藏? 在安卓应用中,用户注册或者登录时,需要把密码隐藏,实现一定的保密效果.在安卓中,可以通过设置EditText组件的TransformationMetho ...

随机推荐

  1. wepack+sass+vue 入门教程(二)

    六.新建webpack配置文件 webpack.config.js 文件整体框架内容如下,后续会详细说明每个配置项的配置 webpack.config.js直接放在项目demo目录下 module.e ...

  2. EntityFramework Core Raw SQL

    前言 本节我们来讲讲EF Core中的原始查询,目前在项目中对于简单的查询直接通过EF就可以解决,但是涉及到多表查询时为了一步到位就采用了原始查询的方式进行.下面我们一起来看看. EntityFram ...

  3. MVC Core 网站开发(Ninesky) 2.1、栏目的前台显示(补充)

    在2.1.栏目的前台显示中因右键没有添加视图把微软给鄙视了一下,后来有仔细研究了一下发现应该鄙视自己,其实这个功能是有的,是自己没搞清楚乱吐糟. 其实只要在NuGet中安装两个包(Microsoft. ...

  4. .Net 大型分布式基础服务架构横向演变概述

    一. 业务背景 构建具备高可用,高扩展性,高性能,能承载高并发,大流量的分布式电子商务平台,支持用户,订单,采购,物流,配送,财务等多个项目的协作,便于后续运营报表,分析,便于运维及监控. 二. 基础 ...

  5. mysql进阶之存储过程

    往往看别人的代码会有这样的感慨: 看不懂 理还乱 是离愁 别是一番滋味在心头 为什么要使用存储过程? 在mysql开发中使用存储过程的理由: 当希望在不同的应用程序或平台上执行相同的函数,或者封装特定 ...

  6. spring boot 部署为jar

    前言 一直在ide中敲代码,使用命令行mvn spring-boot:run或者gradlew bootRun来运行spring boot项目.想来放到prod上面也应该很简单.然而今天试了下,各种问 ...

  7. C# salt+hash 加密

    一.先明确几个基本概念 1.伪随机数:pseudo-random number generators ,简称为:PRNGs,是计算机利用一定的算法来产生的.伪随机数并不是假随机 数,这里的" ...

  8. MyBatis源码分析(二)语句处理器

    StatementHandler 语句处理器,主要负责语句的创建.参数的设置.语句的执行.不负责结果集的处理. Statement prepare(Connection connection, Int ...

  9. 用Swagger生成接口文档

    Swagger简介 在系统设计的时候,各个应用之间往往是通过接口进行交互的.因此接口的定义在整个团队中就变得尤为重要.我们可以把接口的规范用接口描述语言进行描述,然后Swagger可以根据我们定义的接 ...

  10. Oracle 10g安装教程

    首先下载安装文件,打开后文件结构如图所示: 安装之前请关闭Windows防火墙并断开网络. xp系统下直接双击运行(本经验以XP系统安装为例进行讲述). 如果是在win7上安装,如图:在setup文件 ...