0

I'm facing the famous IllegalStateException problem in my app. The problem stems after a network request (using Retrofit) is done, returning me an Observable. I'm using the excellent Mosby framework to handle MVP stuff, here is my code:

public class CarAdPresenterCustomer extends CarAdPresenterAbstract {

    private final GetCarByIdUseCase getCarByIdUseCase;
    private final GetCarMatchDetailsUseCase getCarMatchDetailsUseCaseUseCase;

    public CarAdPresenterCustomer(GetCarByIdUseCase getCarByIdUseCase, GetCarMatchDetailsUseCase getCarMatchDetailsUseCaseUseCase) {
        this.getCarByIdUseCase = getCarByIdUseCase;
        this.getCarMatchDetailsUseCaseUseCase = getCarMatchDetailsUseCaseUseCase;
    }

    public void getCarMatchDetails(String carId) {

        if (isViewAttached()) {
            getView().showLoading();
        }
        getCarMatchDetailsUseCaseUseCase.execute(new GetCarMatchDetailsSubscriber(), new GetCarMatchDetailsUseCase.Params(carId));
    }

    public void getCarDetails(String carId) {
        getCarByIdUseCase.execute(new GetCarByIdSubscriber(), new GetCarByIdUseCase.Params(carId));
    }

    @Override
    public void detachView(boolean retainInstance) {
        getCarByIdUseCase.dispose();
        getCarMatchDetailsUseCaseUseCase.dispose();
        super.detachView(retainInstance);
    }

    private class GetCarByIdSubscriber extends DefaultObserver<DefaultCarResponse> {

        @Override
        public void onNext(DefaultCarResponse carResponse) {
            if (carResponse != null) {
                if (isViewAttached()) {
                    getView().hideLoading();
                    getView().inflateCarUiComponents(carResponse.getCar());
                }
            }
        }

        @Override
        public void onError(Throwable exception) {
            if (isViewAttached()) {
                getView().hideLoading();
                getView().showErrorMessage(exception.getMessage());
            }
        }
    }

The exception I'm getting from Crashlytics is:

Fatal Exception: 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:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:145)
       at android.app.ActivityThread.main(ActivityThread.java:6843)
       at java.lang.reflect.Method.invoke(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Caused by java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
       at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1884)
       at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1902)
       at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:650)
       at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:609)
       at android.support.v4.app.DialogFragment.show(DialogFragment.java:143)
       at br.com.moobie.android.emailConfirmation.ui.fragment.EmailConfirmationFragment.showEmailConfirmationError(EmailConfirmationFragment.java:202)
       at br.com.moobie.android.emailConfirmation.presenter.EmailConfirmationPresenterImpl$GetUserSubscriber.onNext(EmailConfirmationPresenterImpl.java:75)
       at br.com.moobie.android.emailConfirmation.presenter.EmailConfirmationPresenterImpl$GetUserSubscriber.onNext(EmailConfirmationPresenterImpl.java:63)
       at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:200)
       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:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:145)
       at android.app.ActivityThread.main(ActivityThread.java:6843)
       at java.lang.reflect.Method.invoke(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)

My questions: Is it right to dispose Observables in the detachView method ? From what I understand, the detachView is called when the frag (I'm using it with frags) onDestroyView is called, right after onStop (where the state gets checked by Android). If not, should I create a dispose method in my presenter and call it from my frag onStop ?

Thanks !

Leonardo
  • 3,141
  • 3
  • 31
  • 60
  • Honestly I think you don't have many options. I had the same issue. The fragments destroy view method was not called, even though we were using ``replace`` instead of ``add``. I'm not entirely sure, but I think this is related with appcompat activities, which save us a lot of hassle and handle a lot of stuff with fragments that we don't need to care. I've ended up exposing the ``onStart`` and ``onStop`` methods from the fragment so I could properly dispose and query for data. – Fred Jul 03 '17 at 13:43

1 Answers1

0

I think detachView() is the correct event to dispose, if you would dispose at onStop() - where ever your activity stopped the query will cancelled. from UX perspective, that's means that if user started a query, then got a phone call, or switch to another app and comes back, the query will not continue and he must trigger it again and wait inside the app.

as for this problem, even if you would dispose at onStop(), it will not solve the problem as onSaveInstanceState()is called before onStop().

This is a general common Fragments issue, when you commit fragment transaction after onSaveInstance(), and should be solved at Fragments level, you can see here various solutions, like using commitAllowingStateLoss() for instance:
IllegalStateException: Can not perform this action after onSaveInstanceState with ViewPager

yosriz
  • 10,147
  • 2
  • 24
  • 38