Dagger 2 test application

Posted on

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 result
  • autoConnect(0) will start the sequence immediately, even without subscribers yet
  • mDisposable 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.

Leave a Reply

Your email address will not be published. Required fields are marked *