Async Injection in Dagger 2 with RxJava
First version of this post was originally written on my dev-blog: http://frogermcs.github.io/
This post is a part of series of posts showing Dependency Injection with Dagger 2 framework in Android. Today we’re going to take a look at Asynchronous injection 2 with RxJava — alternative for Dagger 2 Producers
Here is the list of previous posts from series:
- Introduction to Dependency Injection
- Dagger 2 API
- Dagger 2 — custom scopes
- Dagger 2 — graph creation performance
- Dependency injection with Dagger 2 — Producers
- Async Injection in Dagger 2 with RxJava (this post)
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 Observable object, not HeavyExternalLibrary. Singleton also prevents from creating additional Observable object.
- @Provides — because this method is also a part of @Module annotated class. Do you remember Dagger 2 API 😉?
- Lazy<HeavyExternalLibrary> heavyExternalLibraryLazy object 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 heavyExternalLibrary object by calling heavyExternalLibraryLazy.get() every time 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 (AndroidSchedulers.mainThread()).
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:
Also our 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:
Our Observable<HeavyExternalLibrary> can be a singleton but every time when we call subscribe() on it, we will get new instance of HeavyExternalLibrary in 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 Observable.
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!