Rxjava 实现 Dagger 2 的异步注入

1,213 阅读3分钟
原文链接: sinyuk.me

Async Injection in Dagger 2 with RxJava

传送门,稍微翻译一下MIROSLAW STANEK的博客,他关于Dagger 2的系列文章写得超好啊🌹

这是讲使用Rxjava实现异步注入的,有兴趣的可以看看他的另外一篇文章:

Producers实现Dagger 2的异步依赖注入

不幸的是Producers不是为Android设计的,并且有一些缺陷:

  • 依赖Guava(很快就会达到64K方法数和让编译变得更慢)
  • 不是特别快(因为注入机制还是会阻塞UI线程)
  • 不使用@Inject注解(那还是算了吧🙂)

安利的工具:AndroidDevMetrics 

  • 检测视图加载的时间同时(如果你在用Dagger 2) 它也会显示构造每一个依赖对象所花费的时间.

但是我们有Rxjava💪


Async @Singleton injection

比如这里是我们要注入的重量级对象:

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

然后我们创建另外一个provide()方法,返回一个异步调用的Observable<HeavyExternalLibrary>对象:

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

来分析一下:

  • 这里提供的不是HeavyExternalLibrary对象,而是一个Observable
  • Lazy<>: 懒加载,如果直接初始化就失去了意义
  • 通过heavyExternalLibraryLazy.get()Observable被订阅的时候初始化,执行在Schedulers.io(),实现异步加载

你可以像平时一样注入这个Observable对象,真正需要的heavyExternalLibrary会在onNext()方法里返回:

public class SplashActivity {

	@Inject
	Observable<HeavyExternalLibrary> heavyExternalLibraryObservable;

	//This will be injected asynchronously
	HeavyExternalLibrary heavyExternalLibrary; 

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

Interface Lazy

懒注入?(好拗口)

在第一次调用get()方法的时候才被注入(初始化),之后每一个调用get()方法都会返回一个相同的对象.

Lazy != Singleton

我们说的每次返回相同的对象是指对同一个@Inject使用get()方法

举个🌰:

 @Module
   final class CounterModule {
     int next = 100;

      @Provides Integer provideInteger() {
       System.out.println("computing...");
       return next++;
     }
   }
  final class LazyCounters {
      @Inject LazyCounter counter1;
      @Inject LazyCounter counter2;

     void print() {
       counter1.print();
       counter2.print();
     }
   }

输出:

 printing...
   computing...
   100
   100
   100
   printing...
   computing...
   101
   101
   101

所以:只有@Singleton会注入同一个对象,Lazy<T>其实每次都会去初始化一个新的对象

Async new instance injection

所以,当我们每次都要注入一个新的实例的时候,不仅仅是不能再用@Singletonscope,而且Lazy<>对象也不再适用了

需要作出这样的改动:

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

Observable<HeavyExternalLibrary> provide()方法:

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

Complete async injection

我们可以用Observable进一步简化注入的过程:

假如我们这样注入:(参考GithubClient的🌰)

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()方法:

@Override
protected void setupActivityComponent() {
    Observable.create(new Observable.OnSubscribe<Object>() {
        @Override
        public void call(Subscriber<? super Object> 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<Object>() {
                @Override
                public void onCompleted() {
                    //Here is the moment when injection is done.
                    analyticsManager.logScreenView(getClass().getName());
                    presenter.callAnyMethod();
                }
            });
}

这样一来,依赖的初始化就不会阻塞UI线程了😄