Good morning, I am recently taken up playing around with RxJava and I am facing a problem.
I have a Fragment in a ViewPager which on onViewCreated() launches an HTTP request (handled by Retrofit and RxJava) to retrieve a list of Strings. The way it does it is by creating a subscription and adding it to a CompositeSubscription object
here is the code
final Subscription subscription = MyManager.getService().getListStringsFromWeb()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>() {
@Override
public void onCompleted() {
// DO NOTHING
}
@Override
public void onError(Throwable e) {
// DO SOMETHING
}
@Override
public void onNext(List<String> result) {
returnResultToView(result);
}
});
addSubscription(subscription);
In the Fragment on onViewCreated() I check for the result being there, if there is not we call the method above, if there is we are happy as a clam.
The problem is that when I rotate the device the "first" subscription has yet to return, therefore the data is not remembered by the fragment and it launches another search, and if I keep rotating I keep adding subscriptions
I would like to know if there is a way to hold on the first subscription and when its result arrives just give it back to the Fragment and disregarding all the other subscriptions
STEP BY STEP DESIRED BEHAVIOR:
Thank you very much
The unfortunate thing regarding Fragments and RxJava is that you have to keep the observable around somehow. One method is to make them static to the Fragment so that you only create them once, then subscribe/unsubscribe in onStart()/onStop() or however you decide.
The alternate approach if you don't like making static variables is to use a retained Fragment which handles the data. The View Fragment which shoes the data can then "bind" itself to the data Fragment. The advantages of this method is that the data is easily available to all components of an Activity, it abstracts away the data from the view for easy testing, and it outlives lifecycle components.
So for a retained fragment you'd do something like this:
public class DataFragment extends Fragment {
private Observable<List<String> stringObs;
@Override
public void onAttach(Context ctx) {
super.onAttach(ctx);
setRetainInstance(true);
}
@Override
public void onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInsatnceState) {
return null; // no view, no problem.
}
public Observable<List<String>> getStrings() {
if (stringsObs == null) {
stringsObs = MyManager.getService()
.getListStringsFromWeb()
.cache()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
}
return stringsObs;
}
}
Add the DataFragment to the FragmentManager like so:
FragmentManager fm = getFragmentManager();
DataFragment df = fm.findFragmentByTag("DataFragment");
if (df == null) {
// Only add the retained fragment if it doesn't exist. Otherwise, use the old one.
fm.beginTransaction()
.add(new DataFragment(), "DataFragment")
.commit();
}
Then on the ViewFragment you can subscribe to it similarly like this:
public class ViewFragment extends Fragment {
private Subscription s;
@Override
public void onStart() {
super.onStart();
DataFragment dataFragment = (DataFragment) getFragmentManager().findFragmentByTag("DataFragment");
s = dataFragment.getStrings()
.subscribe(new Observer<List<String>() {
// Do things
});
}
@Override
public void onStop() {
super.onStop();
s.unsubscribe();
}
}
Now, your ViewFragment can disappear and re-appear on a whim without obstructing the download. It's easier to manipulate the Observables in ways that you need as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With