Problem
I have a complicated Dagger 2 use case on Android, where I have a lot of dependencies, but some of them are really slow to construct. Like 2-3 seconds slow.
My solution was to create an RX Observable
that will be provided through Dagger’s injection mechanism to other components, instead of waiting for the slow component to construct on the main thread.
The app’s Application
object would create the Observable
, and provide access to it via one public getter I place on it. Application instance is a singleton by default, so I thought I could hold the reference there.
Another Observable
would be the one constructing the slow component and pushing it into the publicly-accessible stream.
My code for demonstrating that scenario:
public class Dagger2TestApplication extends Application {
private Subject<SlowComponent> mSlowComponent;
@Override
public void onCreate() {
super.onCreate();
// behavior subject saves the last value inside (great for singleton access)
mSlowComponent = BehaviorSubject.create();
// async slow component loading
Completable
.create(emitter -> {
// prepare an artificial blocking delay
final long delay = 2000L + Math.round(Math.random() * 4000d);
final long start = System.currentTimeMillis();
// noinspection StatementWithEmptyBody - just actively wait here
do {} while (System.currentTimeMillis() - start < delay);
// now create the instance
final SlowComponent instance = DaggerSlowComponent.builder().contextModule(new ContextModule(this)).build();
// finally send the item to all observers, finalize this stream
mSlowComponent.onNext(instance);
emitter.onComplete();
})
.subscribeOn(Schedulers.io())
.subscribe();
}
public Observable<SlowComponent> getSlowComponentObservable() {
return mSlowComponent;
}
}
I decided to use a BehaviorSubject
because it would immediately emit anything that was previously pushed into the stream, making it a holder/wrapper around my singleton dependency graph.
Basically, main main conundrum is – do you think this is the way to go for my use case? Or do you have any other ideas on how to achieve this type of behavior?
Solution
Not sure why you complicated it, a simple set of operators could do the same:
Observable<SlowComponent> mSlowComponent;
SerialDisposable mDisposable;
@Override
public void onCreate() {
super.onCreate();
mDisposable = new SerialDisposable();
mSlowComponent = Observable.fromCallable(() ->
DaggerSlowComponent.builder()
.contextModule(new ContextModule(this))
.build()
)
.subscribeOn(Schedulers.io())
.replay(1)
.autoConnect(0, mDisposable::set);
}
@Override
public void onDestroy() {
super.onDestroy();
mDisposable.dispose();
mSlowComponent = null;
}
Main points:
- Use
fromCallable
for a single element result replay(1)
will keep replaying a that single resultautoConnect(0)
will start the sequence immediately, even without subscribers yetmDisposable
will allow the sequence to be cancelled if the activity gets disposed while the slow component is still being created.
Edit:
I almost forgot, SequentialDisposable
is internal to RxJava, the public version is SerialDisposable
.