0
\$\begingroup\$

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?

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ So this is happening in Application class, so no destroy event is available. But this is already shorter than mine, that's why I am asking the question in the first place :) \$\endgroup\$ – milosmns Sep 27 '17 at 19:18
  • \$\begingroup\$ Okay, then you'll have to leave out mDisposable unfortunately. \$\endgroup\$ – akarnokd Sep 27 '17 at 19:21
  • \$\begingroup\$ Yeah, much safer with it. But OS ending application will also end the process so I'm guessing it's not a big issue. I'll check out the autoConnect(), wasn't aware of that one \$\endgroup\$ – milosmns Sep 27 '17 at 19:23
  • \$\begingroup\$ Also one more question - what would happen if I moved replay() above the subscribeOn() call? \$\endgroup\$ – milosmns Sep 28 '17 at 10:39
  • \$\begingroup\$ I'm marking this as correct answer anyway, just discussing it further \$\endgroup\$ – milosmns Sep 28 '17 at 10:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.