Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrofit + RxJava - Not emitting when Fragment is restored, if I used Disposable.dispose()

I have a Fragment A that uses Retrofit to calls an API in onCreateView.
The result will be used to display a list of objects.

Fragment A also has a button which navigates to Fragment B, by commiting a FragmentTransaction and replace itself. (Fragment A onDestroyView triggered)

Everything works well, but when I back from Fragment B to Fragment A(Fragment A onCreateView triggered), API is not called.

This only happens if I call Disposable.dispose() in onDestroyView, which is an attempt to prevent memory leaks.

What I expect

Since new Observables will be created every time onCreateView is triggered, they are supposed to be indenpendent of the previously disposed Disposables. And therefore API will be called again and callbacks will be triggered. The list of object should be refreshed.

What I observed

onCreateView is triggered, the line which tells Retrofit to call API is also triggered. But none of the callbacks (including .map()) are triggered. Since I have also logged network traffics, I am pretty sure that no network communication has actually been made. It looks like either RxJava or Retrofit decided to stop the stream.

The Question

Of course this can be avoided if I do not use Disposable.dispose(). But I do want to prevent memory leaks.
What is the correct way to do this?

My Code (Simplified)

Fragment A:

public class BeansFragment extends BaseFragment {

   ...

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        ...
        retrofit.getSomeBeans()
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new APIResponseObserver<List<Beans>>(this) {
                    @Override
                    public void onNext(List<Beans> beans) {
                        if (getContext() == null) return;
                        setupBeansList(beans);
                    }
                });
        ...
    }

    ...
}

BaseFragment:

public abstract class BaseFragment extends Fragment {

    private CompositeDisposable compositeDisposable = new CompositeDisposable();

    ...

    @Override
    public void onDestroyView() {
        compositeDisposable.dispose();
        super.onDestroyView();
    }
}

Most Importantly, APIResponseObserver which does the job of adding the disposable:

public abstract class APIResponseObserver<T> implements io.reactivex.Observer<T> {

    private BaseFragment fragment;

    public APIResponseObserver(BaseFragment fragment) {
        this.fragment = fragment;
    }

    @Override
    public void onSubscribe(Disposable d) {
        if (fragment != null) fragment.addToCompositeDisposable(d);
    }

    @Override
    public void onError(Throwable e) {
        //...
    }

    @Override
    public void onComplete() {
        //...
    }
}

Note

I have also tried to use RxLifecycle, but produced the same result.

like image 926
Sira Lam Avatar asked Mar 06 '23 10:03

Sira Lam


1 Answers

Just found out that I should have used Disposable.clear() instead of dispose(). dispose() made me no longer be able to add Disposable to the same CompositeDisposable.

An interesting fact is, RxLifecycle produces the same problem. Does it mean they use dispose() instead of clear()?

EDIT@2018-04-20

For RxLifecycle, you may refer to this Github issue.

like image 128
Sira Lam Avatar answered Mar 10 '23 11:03

Sira Lam