Async Injection in Dagger 2 with RxJava
A couple weeks ago I wrote a post about asynchronous dependency injection in Dagger 2 with Producers. Objects initialization execution on background thread(s) has one great advantage - it doesn’t block main thread which is responsible for drawing UI in real-time (60 frames per second for keeping smooth interface).
It’s worth pointing out, that slow initialization in not everyone’s issue. But even if you really care about it in your code there is always a chance that any external library does disc/network operations in constructor or any
init() method. If you are not sure about it I suggest you to give a try AndroidDevMetrics - my performance metrics library for Android. It can tell you how much time was needed to show particular screens in app and (if you use Dagger 2) how much time was consumed to provide each object in you dependencies graph.
Unfortunately Producers are not designed for Android and have a couple flaws:
- Use Guava as a dependency (can cause 64k methods issue and increase build time)
- Aren’t extremely fast (injection mechanisms can block main thread for a few to dozens of milliseconds depending on device)
- Don’t use @Inject annotation (a little more mess in code)
While we cannot do much with last two, the first one can compromise Producers in Android projects.
Async injection with RxJava
Fortunately a lot of Android devs use RxJava (and RxAndroid) for creating asynchronous code in our apps. Let’s try to use it in Dagger 2 asynchronous injection.
Async @Singleton injection
Here is our heavy object:
Now let’s create additional
provide...() method which returns
Observable<HeavyExternalLibrary> object, which will call this code asynchronously:
Let’s analyze it line by line:
@Singleton- it’s important to keep in mind that it will be single instance of
HeavyExternalLibrary. Singleton also prevents from creating additional Observable object.
@Provides- because this method is also a part of
@Moduleannotated class. Do you remember Dagger 2 API 😉?
Lazy<HeavyExternalLibrary> heavyExternalLibraryLazyobject prevents from initializing HeavyExternalLibrary object internally by Dagger (otherwise in a moment of calling
provideHeavyExternalLibraryObservable()object would be already created).
Observable.create(...)code - it will return
heavyExternalLibraryobject by calling
heavyExternalLibraryLazy.get()everytime when this Observable will be subscribed.
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());- by default RxJava code is executed on a thread in which Observable is created. That’s why we should move execution to background thread (
Schedulers.io()in this case) and just watch for the results on main thread (
Our Observable is injected like any other object in graph. But object
heavyExternalLibrary itself will be available a bit later:
Async new instance injection
Code above presented how to inject singleton objects. What if we want to inject asynchronously new instances?
Make sure that our object is not @Singleton annotated anymore:
Observable<HeavyExternalLibrary> provider method should be changed a bit. We cannot use
Lazy<HeavyExternalLibrary> because it creates new instance only for the first time when
get() method is called (see Lazy documentation for more details).
Here is the updated code:
Observable<HeavyExternalLibrary> can be a singleton but everytime when we call subscribe() on it, we will get new instance of
HeavyExternalLibraryin onNext() call:
Complete async injection
There is another way to do asynchronous injection in Dagger 2 with RxJava. We can just simply wrap whole injection process with
Let’s say our injection is performed in this way (code is taken from GithubClient example project):
To make it asynchronous we just need to wrap
setupActivityComponent() method with Observable:
As it’s explained, all
@Inject annotated objects will be injected in some time in the future. In return injection process will be asynchronous and won’t have big impact on main thread.
Of course creating
Observable objects and additional threads for
subscribeOn() is not completely free - it will also take some time. It’s pretty similar to impact generated by Producers code.
Thanks for reading!