My callback (see onResume() below) is still getting called even after calling dispose() in onPause() of MyFragment.java. Why?
I don't think it's important, but: I call NetworkUtils.subscribeToAvgPriceUpdates() from multiple Fragments (only one is visible at once). In each Fragment I have one DisposableObserver and then when I switch to that fragment, I subscribe to data updates using that observer.
NetworkUtils.java:
public static void subscribeToAvgPriceUpdates(DisposableObserver<List<Result>> observer, CoinPriceUpdateCallback callback) {
if(observer != null && !observer.isDisposed())
observer.dispose();
observer = new DisposableObserver<List<Result>>() {
#Override
public void onNext(List<Result> results) {
callback.onUpdated(results);
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() { }
};
MainApplication.apiProvider.getAPI().getAllTickersRx()
.repeatWhen(objectObservable -> objectObservable.delay(15000, TimeUnit.MILLISECONDS) )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
MyFragment.java:
private DisposableObserver<List<Result>> tickerUpdateObserver;
#Override
public void onPause () {
if(tickerUpdateObserver != null)
tickerUpdateObserver.dispose();
super.onPause();
}
#Override
public void onResume() {
super.onResume();
NetworkUtils.subscribeToAvgPriceUpdates(tickerUpdateObserver, results -> {
// still getting called even after I switch to another fragment, why?
// shouldn't .dispose() in onPause() stop the updates?
});
}
The problem is that you create a new DisposableObserver inside the method but the field tickerUpdateObserver remains the same first instance, thus you don't have a reference to the newer observers.
You could just return the new DisposableObserver from the method and update the field:
public static DisposableObserver<List<Result>> subscribeToAvgPriceUpdates(
DisposableObserver<List<Result>> observer,
CoinPriceUpdateCallback callback) {
if(observer != null && !observer.isDisposed())
observer.dispose();
observer = new DisposableObserver<List<Result>>() {
#Override
public void onNext(List<Result> results) {
callback.onUpdated(results);
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() { }
};
MainApplication.apiProvider.getAPI().getAllTickersRx()
.repeatWhen(objectObservable ->
objectObservable.delay(15000, TimeUnit.MILLISECONDS) )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
return observer;
}
#Override
public void onResume() {
super.onResume();
tickerUpdateObserver = NetworkUtils.subscribeToAvgPriceUpdates(
tickerUpdateObserver, results -> {
// still getting called even after I switch to another fragment, why?
// shouldn't .dispose() in onPause() stop the updates?
});
}
Related
I want to set a count down timer inside each event in recyclerview.
I tried it inside onBindViewHolder. It is working but at the same time it is also affecting the UI.
For example, click events are not working.
Is the any best approach to resolve it?
You could use Observable.interval from RxJava
Here's a util function for countdowntimer
public static Observable<Long> countdownTimer(long countdownValue, Observer<Long> observer) {
if (observer == null) return Observable.just((long) 0);
//.interval is a cold observable
// it will emit separately for every observer
Observable<Long> timerObservale = Observable
.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.takeWhile(new Predicate<Long>() {
#Override
public boolean test(Long aLong) throws Exception {
return aLong <= countdownValue;
}
})
.doOnError(Throwable::printStackTrace)
.observeOn(AndroidSchedulers.mainThread());
timerObservale.subscribe(observer);
return timerObservale;
}
Here's how you would use it
Utils.countdownTimer(60, new Observer<Long>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Long aLong) {
//do somethng
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
The utils function emits a long every second for a given period of time
This is my code snippet.
#Override
public void onDestroy() {
super.onDestroy();
disposableSingleObserver.dispose();
}
/**
* fetches json by making http calls
*/
private void fetchContacts() {
disposableSingleObserver = new DisposableSingleObserver<List<Contact>>() {
#Override
public void onSuccess(List<Contact> movies) {
//Toast.makeText(getActivity(), "Success", Toast.LENGTH_SHORT).show();
contactList.clear();
contactList.addAll(movies);
mAdapter.notifyDataSetChanged();
// Received all notes
}
#Override
public void onError(Throwable e) {
// Network error
}
};
// Fetching all movies
apiService.getContacts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(disposableSingleObserver);
}
I am getting a warning The result of subscribeWith is not used.
What is the correct way to fix this?
when you use subscribeWith(), it returns Disposable it self through which you can dispose the subscription at any time if you want. That eliminates your need to create a disposable for storing it in variable to dispose later. You can do something like this:
// Declare global disposable variable
private CompositeDisposable mDisposable = new CompositeDisposable();
// Then add the disposable returned by subscribeWith to the disposable
mDisposable.add(apiService.getContacts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(disposableSingleObserver));
And at any point of time, if you want to cancel the API call, then you can simply call like in onDestroy()
mDisposable.clear();
OR
more similar to the way you did it currently like: (Notice that the result of subscribeWith() is directly assigned to your disposableSingleObserver variable
disposableSingleObserver = apiService.getContacts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<List<Contact>>() {
#Override
public void onSuccess(List<Contact> movies) {
//Toast.makeText(getActivity(), "Success", Toast.LENGTH_SHORT).show();
contactList.clear();
contactList.addAll(movies);
mAdapter.notifyDataSetChanged();
// Received all notes
}
#Override
public void onError(Throwable e) {
// Network error
}
});
And dispose it in onDestroy like you are doing currently:
#Override
public void onDestroy() {
super.onDestroy();
disposableSingleObserver.dispose();
}
I'm using RxJava2 and Retrofit. In my fragment, I make a request to upload a local file:
Disposable disposable = mApi.requestUpload(file)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
toast("success");
}, throwable -> {
toast("failed");
});
mCompositeDisposable.add(disposable);
Then, clear all disposables in onDestroyView()
#Override
public void onDestroyView() {
mCompositeDisposable.clear();
super.onDestroyView();
}
But I use Charles to view all requests and find that the request is still executing after I finish the fragment. The file is still uploaded successfully after a time.
How can I cancel the call when fragment closed?
use disposable.dipose() for canceling your call.
Define Disposable
private io.reactivex.disposables.Disposable mDisposable;
assign to your service
mService.getResults(query)
.observeOn(AndroidSchedulers.from(Looper.getMainLooper(), true))
.subscribeOn(Schedulers.io())
.subscribe(new SingleObserver<Response<Model>>() {
#Override
public void onSubscribe(Disposable d) {
/* required */
mDisposable = d;
}
#Override
public void onSuccess(Response<Model> response) {
dismissProgress();
}
#Override
public void onError(Throwable e) {
}
});
call below line to dismiss/terminate ongoing API call
if (mDisposable != null)
mDisposable.dispose();
There you go.
UPDATE
in your case, it should be
#Override
public void onDestroyView() {
if (mCompositeDisposable!= null) // in case if you required-> if (mCompositeDisposable!= null && !mCompositeDisposable.isDisposed())
mCompositeDisposable.dispose();
super.onDestroyView();
}
Add RxJava to CompositeDisposable
Then in onStop()
use disposableRxJava.dispose()
I have an Activity which loads data from the network and has a "retry" button if the request fails which just re-makes the same network call. This is the simplified code:
public class MainActivity extends Activity {
private DisposableObserver<Data> disposableObserver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
loadData();
}
});
}
private void loadData() {
disposableObserver = control.fetchFromNetwork().subscribeWith(new DisposableObserver<Data>() {
#Override
public void onNext(Data data) {
updateUI(data);
}
#Override
public void onError(Throwable throwable) {
showError();
}
#Override
public void onComplete() {
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
if (disposableObserver != null && !disposableObserver.isDisposed()) {
disposableObserver.dispose();
}
}
}
For what it's worth, this is the method that creates the Observer:
public Observable<Data> fetchFromNetwork() {
return getService().fetchdata()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new Consumer<Throwable>() {
#Override
public void accept(Throwable t) throws Exception {
exceptionHandler.handle(t);
}
});
}
I'm using a DisposableObserver so it can be properly disposed of in the Activity's onDestroy() method.
In this code, every button click will create a new Observable and subscribe to it, creating a leak since only the last one is disposed of in the onDestroy() method. My question is: is there a way to retry/replay this same observer which already exists without having to create a new one every time? Or, is there a better approach to this scenario?
So you need to create a newObservable every time, to avoid the leaking issue you can create a CompositeDisposable and use the add method that receives the Disposable created after calling the .subscribe(). Then on onDestroy() simply call clear() and it will dispose every not disposed Disposable.
I have started learning RxAndroid and below is the code I wrote to iterate over a model object (Results) that contains data fetched from the server. I'm iterating over the model object in the observable and providing a newly created object in the observer. I'm trying to take subscription of the observer to unsubscribe the task upon Orientation changes of the fragment. However the subscribe() returns VOID instead of subscription object.
Questions:
Does the latest version of RxAndroid handle unsubscription itself upon configuration/orientation change?
In case configuration change happens before the task is complete, the only way to restart this task that I can think of is, I persist the server response in onSavedInstance() and retrieve it from bundle when the fragment is recreated. It'll require booleans to figure out if the configuration change happened before the configuration change or not. Is there a graceful and cleaner way of coping with this?
private void createComicList(final List<Result> marvelResults) {
final MarvelComics marvelComics = new MarvelComics();
Observable marvelObservable2 = Observable.create(new ObservableOnSubscribe<MarvelComic>() {
#Override
public void subscribe(ObservableEmitter<MarvelComic> e) throws Exception {
for(Result result : marvelResults) {
MarvelComic marvelComic = new MarvelComic();
marvelComic.setDescription(result.getDescription());
marvelComic.setTitle(result.getTitle());
marvelComic.setPageCount(result.getPageCount());
marvelComic.setThumbnailUrl(result.getThumbnail().getPath());
marvelComic.setId(result.getId());
e.onNext(marvelComic);
}
e.onComplete();
}
});
marvelObservable2.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MarvelComic>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(MarvelComic comic) {
marvelComics.getMarvelComicList().add(comic);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
showToast();
}
});
}
The Observable.subscribe(Observer<? super T>) method returns void in the 2.x since the Observer.onSubscribe(Disposable) is there to get the cancellation support that used to be Subscription in 1.x.
final CompositeDisposable composite = new CompositeDisposable();
Observable<Integer> source = Observable.just(1)
source.subscribe(new Observer<Integer>() {
#Override public void onSubscribe(Disposable d) {
composite.add(d); // <---------------------------------------------
}
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
composite.add(source
.subscribeWith( // <-----------------------------------------------
new DisposableObserver<Integer>() {
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
subscribe() method of Observable returns Subscription object in earlier versions of RxJava and current version returns an object of Disposble class which you can unsubscribe by invoking dispose() method.
For your second question you may check this answer Best practice: AsyncTask during orientation change