sockeqwe / mosby

A Model-View-Presenter / Model-View-Intent library for modern Android apps
http://hannesdorfmann.com/mosby/
Apache License 2.0
5.49k stars 841 forks source link

[SOLVED?] `sample-mvi` crashes on Start #314

Open ValeriusGC opened 6 years ago

ValeriusGC commented 6 years ago

When i start it on Android Studio 3.1.2, app crashes with stack like this:

05-28 10:23:15.357 28499-28499/com.hannesdorfmann.mosby3.sample.mvi E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.hannesdorfmann.mosby3.sample.mvi, PID: 28499
    java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:111)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6119)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
     Caused by: java.lang.IllegalStateException: ViewState observable must not reach error state - onError()
        at com.hannesdorfmann.mosby3.mvi.DisposableViewStateObserver.onError(DisposableViewStateObserver.java:22)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:276)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
        at android.os.Handler.handleCallback(Handler.java:751) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6119) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
     Caused by: java.lang.IllegalStateException: Only a single observer allowed.
        at io.reactivex.subjects.UnicastSubject.subscribeActual(UnicastSubject.java:150)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.subscribeInner(ObservableFlatMap.java:162)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onNext(ObservableFlatMap.java:139)
        at io.reactivex.internal.operators.observable.ObservableFromArray$FromArrayDisposable.run(ObservableFromArray.java:107)
        at io.reactivex.internal.operators.observable.ObservableFromArray.subscribeActual(ObservableFromArray.java:36)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.internal.operators.observable.ObservableConcatMap$ConcatMapDelayErrorObserver.drain(ObservableConcatMap.java:467)
        at io.reactivex.internal.operators.observable.ObservableConcatMap$ConcatMapDelayErrorObserver.onSubscribe(ObservableConcatMap.java:325)
        at io.reactivex.internal.operators.observable.ObservableFromArray.subscribeActual(ObservableFromArray.java:30)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.internal.operators.observable.ObservableConcatMap.subscribeActual(ObservableConcatMap.java:54)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.internal.operators.observable.ObservableCombineLatest$LatestCoordinator.subscribe(ObservableCombineLatest.java:119)
        at io.reactivex.internal.operators.observable.ObservableCombineLatest.subscribeActual(ObservableCombineLatest.java:73)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.internal.operators.observable.ObservableObserveOn.subscribeActual(ObservableObserveOn.java:45)
        at io.reactivex.Observable.subscribe(Observable.java:10700)
        at io.reactivex.Observable.subscribeWith(Observable.java:10751)
        at com.hannesdorfmann.mosby3.mvi.MviBasePresenter.subscribeViewState(MviBasePresenter.java:366)
        at com.hannesdorfmann.mosby3.sample.mvi.view.shoppingcartoverview.ShoppingCartOverviewPresenter.bindIntents(ShoppingCartOverviewPresenter.java:108)
        at com.hannesdorfmann.mosby3.mvi.MviBasePresenter.attachView(MviBasePresenter.java:248)
        at com.hannesdorfmann.mosby3.FragmentMviDelegateImpl.onStart(FragmentMviDelegateImpl.java:161)
        at com.hannesdorfmann.mosby3.mvi.MviFragment.onStart(MviFragment.java:91)
        at android.support.v4.app.Fragment.performStart(Fragment.java:2287)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1458)
        at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1750)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1819)
        at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3227)
        at android.support.v4.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:3186)
        at android.support.v4.app.FragmentController.dispatchStart(FragmentController.java:203)
        at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:582)
        at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:177)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1248)
        at android.app.Activity.performStart(Activity.java:6696)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2628)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
        at android.os.Handler.dispatchMessage(Handler.java:102)
            ... 5 more
ValeriusGC commented 6 years ago

Here is the change i made to fix this problem: MviBasePresenter.java

        Subject<I> intentRelay = ReplaySubject.create();
        //Subject<I> intentRelay = UnicastSubject.create();

this was inserted on 513d1e9 ("use unicast subject", 2018-04-08)

sockeqwe commented 6 years ago

Sorry for the long delay. I'm quite busy at the moment, I will take a look at this at the end of July. As far as I can tell is that all automated tests (testing Mosby internals) are passing. Hence (without having investigated into this bug) I have the feeling that this is a Sample App issue.

p8499 commented 5 years ago

When I upgrade mosby library from 3.1.0 to 3.1.1 I faced with this problem too.

p8499 commented 5 years ago

Maybe UnicastSubject conflicts with the navigation feature, stateObservable.share().map{...}.addTo(compositeDisposable) thus makes it subscribed by two observables.

drbear95 commented 5 years ago

Im facing this problem in my app, i tried with ReplaySubject but it doesn't work.

sockeqwe commented 5 years ago

What exactly is the issue that you face in your app?

Simon notifications@github.com schrieb am Do., 30. Mai 2019, 12:34:

Im facing this problem in my app, i tried with ReplaySubject but it doesn't work.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/sockeqwe/mosby/issues/314?email_source=notifications&email_token=AAEOPLSY7KK4YJEL66G4CWTPX6UUPA5CNFSM4FB6S7PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWR7VPY#issuecomment-497285823, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEOPLUG3OADVNMS3BVWNGLPX6UUPANCNFSM4FB6S7PA .

drbear95 commented 5 years ago

Here is my stack trace

 java.lang.IllegalStateException: ViewState observable must not reach error state - onError()
        at com.hannesdorfmann.mosby3.mvi.DisposableViewStateObserver.onError(DisposableViewStateObserver.java:22)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:281)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172)
        at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:255)
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:119)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
     Caused by: java.lang.IllegalStateException: Only a single observer allowed.
        at io.reactivex.subjects.UnicastSubject.subscribeActual(UnicastSubject.java:311)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableSwitchMap.subscribeActual(ObservableSwitchMap.java:51)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:32)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.subscribeInner(ObservableFlatMap.java:165)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onNext(ObservableFlatMap.java:139)
        at io.reactivex.internal.operators.observable.ObservableFromArray$FromArrayDisposable.run(ObservableFromArray.java:108)
        at io.reactivex.internal.operators.observable.ObservableFromArray.subscribeActual(ObservableFromArray.java:37)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.subscribeInner(ObservableFlatMap.java:165)
        at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onNext(ObservableFlatMap.java:139)
        at io.reactivex.internal.operators.observable.ObservableFromArray$FromArrayDisposable.run(ObservableFromArray.java:108)
        at io.reactivex.internal.operators.observable.ObservableFromArray.subscribeActual(ObservableFromArray.java:37)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableScanSeed.subscribeActual(ObservableScanSeed.java:47)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
        at io.reactivex.Observable.subscribe(Observable.java:12090)
        at io.reactivex.internal.operators.observable.ObservableDistinctUntilChanged.subscribeActual(ObservableDistinctUntilChanged.java:35)
        at io.reactivex.Observable.subscribe(Observable.java:12090)

And here is my base presenter class

abstract class BasePresenter<TView : BaseView<TViewState>, TViewState: ViewState>(viewState: TViewState)
    : MviBasePresenter<TView, TViewState>(viewState) {

    protected val initialState: TViewState = viewState
    private var viewStateSnapshot: TViewState = viewState

    protected val lastViewState: TViewState
        get() = viewStateSnapshot

    override fun attachView(view: TView) {
        super.attachView(view)

        view.render(viewStateSnapshot)
    }

    override fun subscribeViewState(viewStateObservable: Observable<TViewState>, consumer: ViewStateConsumer<TView, TViewState>) {
        val viewStateStream = if(BuildConfig.DEBUG){
            viewStateObservable
        } else {
            viewStateObservable.map { it as ViewState }
                    .onErrorReturn {
                        FailureViewState()
                    }.filter { it !is FailureViewState }
                    .map { it as TViewState }
        }

        super.subscribeViewState(viewStateStream) { view, viewState ->
            this.viewStateSnapshot = viewState
            consumer.accept(view, viewState)
        }
    }

    private class FailureViewState: ViewState()
}

After changing Unicast to Relay (as ValeriusGC suggest) state is not beeing updated anymore.

sockeqwe commented 5 years ago

Oh ... i see, thanks for reporting! I have to think about it. It seems I have to write something custom to handle this cases ...

Simon notifications@github.com schrieb am Sa., 1. Juni 2019, 16:28:

Here is my stack trace

java.lang.IllegalStateException: ViewState observable must not reach error state - onError() at com.hannesdorfmann.mosby3.mvi.DisposableViewStateObserver.onError(DisposableViewStateObserver.java:22) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:281) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172) at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:255) at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:119) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) Caused by: java.lang.IllegalStateException: Only a single observer allowed. at io.reactivex.subjects.UnicastSubject.subscribeActual(UnicastSubject.java:311) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableSwitchMap.subscribeActual(ObservableSwitchMap.java:51) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:32) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.subscribeInner(ObservableFlatMap.java:165) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onNext(ObservableFlatMap.java:139) at io.reactivex.internal.operators.observable.ObservableFromArray$FromArrayDisposable.run(ObservableFromArray.java:108) at io.reactivex.internal.operators.observable.ObservableFromArray.subscribeActual(ObservableFromArray.java:37) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.subscribeInner(ObservableFlatMap.java:165) at io.reactivex.internal.operators.observable.ObservableFlatMap$MergeObserver.onNext(ObservableFlatMap.java:139) at io.reactivex.internal.operators.observable.ObservableFromArray$FromArrayDisposable.run(ObservableFromArray.java:108) at io.reactivex.internal.operators.observable.ObservableFromArray.subscribeActual(ObservableFromArray.java:37) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:55) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableScanSeed.subscribeActual(ObservableScanSeed.java:47) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42) at io.reactivex.Observable.subscribe(Observable.java:12090) at io.reactivex.internal.operators.observable.ObservableDistinctUntilChanged.subscribeActual(ObservableDistinctUntilChanged.java:35) at io.reactivex.Observable.subscribe(Observable.java:12090)

And here is my base presenter class

abstract class BasePresenter<TView : BaseView, TViewState: ViewState>(viewState: TViewState) : MviBasePresenter<TView, TViewState>(viewState) {

protected val initialState: TViewState = viewState
private var viewStateSnapshot: TViewState = viewState

protected val lastViewState: TViewState
    get() = viewStateSnapshot

override fun attachView(view: TView) {
    super.attachView(view)

    view.render(viewStateSnapshot)
}

override fun subscribeViewState(viewStateObservable: Observable<TViewState>, consumer: ViewStateConsumer<TView, TViewState>) {
    val viewStateStream = if(BuildConfig.DEBUG){
        viewStateObservable
    } else {
        viewStateObservable.map { it as ViewState }
                .onErrorReturn {
                    FailureViewState()
                }.filter { it !is FailureViewState }
                .map { it as TViewState }
    }

    super.subscribeViewState(viewStateStream) { view, viewState ->
        this.viewStateSnapshot = viewState
        consumer.accept(view, viewState)
    }
}

private class FailureViewState: ViewState()

}

After changing Unicast to Relay (as ValeriusGC suggest) state is not beeing updated anymore.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/sockeqwe/mosby/issues/314?email_source=notifications&email_token=AAEOPLXML25TJBALTEUBZOTPYKBQDA5CNFSM4FB6S7PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWXBVXQ#issuecomment-497949406, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEOPLQRLI7ZGXQBMQRAS5DPYKBQDANCNFSM4FB6S7PA .

drbear95 commented 5 years ago

When can I expect a fix? It is critical for my app because i cannot update to AndroidX

sockeqwe commented 5 years ago

Not before next week

Simon notifications@github.com schrieb am Mo., 3. Juni 2019, 10:16:

When can I expect a fix? It is critical for my app because i cannot update to AndroidX

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/sockeqwe/mosby/issues/314?email_source=notifications&email_token=AAEOPLTHAXDGMS6KLKBVEVTPYTHPNA5CNFSM4FB6S7PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWYU2KA#issuecomment-498158888, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEOPLVTVQDUL7NOZYE4L7DPYTHPNANCNFSM4FB6S7PA .

drbear95 commented 5 years ago

What is the status of this case?

michalmatusiak commented 5 years ago

@sockeqwe any news? when it will be fixed?

sockeqwe commented 5 years ago

No update yet sorry. Do you have custom subscription management somewhere in your presenter?

Michal notifications@github.com schrieb am Mo., 15. Juli 2019, 14:18:

@sockeqwe https://github.com/sockeqwe any news? when it will be fixed?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sockeqwe/mosby/issues/314?email_source=notifications&email_token=AAEOPLSQJU3KLNH4DP76MOLP7RTKDA5CNFSM4FB6S7PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODZ5QLFA#issuecomment-511378836, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEOPLTSP6ZS46IGJQC5NFTP7RTKDANCNFSM4FB6S7PA .

SunMaungOo commented 5 years ago

@sockeqwe any update on the fix? I also got the same problem using AndroidX with version 3.1.1

sockeqwe commented 5 years ago

need to dedicate 1-2 weeks for it, not sure when I will have time for it

Sun Maung Oo notifications@github.com schrieb am Mi., 18. Sep. 2019, 01:03:

@sockeqwe https://github.com/sockeqwe any update on the fix? I also got the same problem using AndroidX with version 3.1.1

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sockeqwe/mosby/issues/314?email_source=notifications&email_token=AAEOPLXUHAGLEC77BPG3NJDQKFO2ZA5CNFSM4FB6S7PKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD66E55Y#issuecomment-532434679, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEOPLSSRHTES5TIXGJRZCTQKFO2ZANCNFSM4FB6S7PA .

SunMaungOo commented 5 years ago

@sockeqwe Just for your information , the solution provided by ValeriusGC which is changing it to ReplaySubject fix the problem. I don't know what effect it will have on other components but it fix the problem described.

paul-wag commented 2 years ago

Wish this had a solution.....