uber / AutoDispose

Automatic binding+disposal of RxJava streams.

Home Page:https://uber.github.io/AutoDispose/

Repository from Github https://github.comuber/AutoDisposeRepository from Github https://github.comuber/AutoDispose

ViewScopeProvider throws when Activity restarts.

littledot opened this issue · comments

Library version:

com.uber.autodispose2:autodispose-androidx-lifecycle:2.0.0

Repro steps or stacktrace:

Start the following Activity:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        Observable.interval(1, TimeUnit.SECONDS)
            .autoDispose(findViewById(R.id.img1))
            .subscribe()
    }
}

Rotate the device to landscape mode to cause a config change.
Observe the following crash:

    io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | autodispose2.OutsideScopeException: View is not attached!
        at io.reactivex.rxjava3.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:717)
        at io.reactivex.rxjava3.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:714)
        at io.reactivex.rxjava3.internal.observers.LambdaObserver.onError(LambdaObserver.java:77)
        at autodispose2.HalfSerializer.onError(HalfSerializer.java:148)
        at autodispose2.AutoDisposingObserverImpl.onError(AutoDisposingObserverImpl.java:98)
        at autodispose2.AutoDisposingObserverImpl$1.onError(AutoDisposingObserverImpl.java:55)
        at autodispose2.android.DetachEventCompletable.subscribe(DetachEventCompletable.java:53)
        at io.reactivex.rxjava3.internal.operators.completable.CompletableDefer.subscribeActual(CompletableDefer.java:43)
        at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2851)
        at autodispose2.AutoDisposingObserverImpl.onSubscribe(AutoDisposingObserverImpl.java:66)
        at io.reactivex.rxjava3.internal.operators.observable.ObservableInterval.subscribeActual(ObservableInterval.java:41)
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13095)
        at autodispose2.AutoDisposeObservable.subscribeActual(AutoDisposeObservable.java:34)
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13095)
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13081)
        at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:12996)
        at autodispose2.AutoDispose$1$4.subscribe(AutoDispose.java:295)
        at com.littledot.androidbugreport.MainActivity.onCreate(MainActivity.kt:20)

Can you confirm if the view is actually attached?

+1
I'd confirm that first. It's common that in onCreate, the view is not attached. To make sure, you can do view.doOnLayout { // Subscribe in here } or view.post {} so that the view is definitely attached before you start a subscription with it's lifecycle.

Could you explain why is it a requirement for the View to be attached? Before autodispose or rxjava, devs could still reference View and touch its properties in onCreate() when Activity restarts.

The same reason we require a fragment (or similar) to be created before you can subscribe. Eagerly subscribing is usually a code smell for one of two reasons:

  • You're subscribing in a constructor but not guaranteed your UI piece will actually start/attach/etc.
  • If you are intentionally subscribing eagerly, you usually don't want to use AutoDispose for that component. If you want to just scope until first detach, you can use something like RxBinding's View#detaches() + take(1).ignoreElements() as your scope rather than ViewScopeProvider

Thanks for the explanation. Since this is expected I'll close this.