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();
}
Related
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 am new to reactive programming (RxJava and RxAndroid). I want to use RxView.clicks() instead of a click Listener. I put a Button into main layer and with Butterknife and in onCreate method Main activity I write this statement:
**//onCreate**
ButterKnife.bind(this);
RxView.clicks(btn_range)
.switchMap(new Function<Object, Observable<Integer>>() {
#Override
public Observable<Integer> apply(Object o) throws Exception {
return Observable.range(1,10);
}
})
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
d.dispose();
}
#Override
public void onNext(Integer integer) {
Toast.makeText(MainActivity.this, integer+"", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
Toast.makeText(MainActivity.this, e.getMessage()+"", Toast.LENGTH_SHORT).show();
}
#Override
public void onComplete() {
}
});
but when I run it, no toast appears.
I have converted my click listener to an observable and then I have changed the observable to a range of integer and finally I display it.
In your .subscribe() the Observer<Integer> calls d.dispose() as soon as it is subscribed.
So if your chain is disposed then it is not working anymore. The Disposable should be disposed when you no longer need the flow.
You could store the emitted Disposable and dispose it in the opposite lifecycle event callback to where you have subscribed it.
I am using RxJava2 and Retrofit2 for handling network requests.
I have cycle where doOnNext should always be ran, but my Activity which is the observer calls dispose() when it is destroyed and that causes retrofit to cancel the request.
java.io.IOException: Canceled
Is there a way to let the request complete but only dispose the UI level observer?
mApi.doSomethingImportant()
.doOnNext(new Consumer<ImportantResponse>() {
#Override
public void accept(ImportantResponse response) throws Exception {
// Store data, should always get here if request is success
}
})
.doOnError(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
// Store error, should always get here if request fails
}
})
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(AndroidSchedulers.mainThread())
.subscribe(observer); // observer reports success/fail on UI if not disposed
Thanks.
I found one way to achieve the wanted behaviour by wrapping the observer with another, but I'm sure there is some elegant way to do this.
...
.subscribe(new NonDisposableObserver<>(observer)
Where NonDisposableObserver class is following:
public class NonDisposableObserver<T> implements Observer<T> {
private DisposableObserver<T> observer;
public NonDisposableObserver(DisposableObserver<T> observer) {
this.observer = observer;
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(T t) {
if (!observer.isDisposed()) {
observer.onNext(t);
}
}
#Override
public void onError(Throwable e) {
if (!observer.isDisposed()) {
observer.onError(e);
}
}
#Override
public void onComplete() {
if (!observer.isDisposed()) {
observer.onComplete();
}
}
}
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
I have 2 Observables that do 2 different jobs that returns their observables
First one : SyncDoctors for getting doctor list from my WebService
public Observable<List<Doctor>> SyncDoctors(){
Observable<List<Doctor>> observable = MyWebService.getInterface().GetAllDoctors();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<List<Doctor>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Doctor> doctors) {
if(doctors.size() == 0){
logger.debug("No Coming Doctors");
return;
}
DoctorDao doctorDao = MyApplication.getDaoSession().getDoctorDao();
doctorDao.deleteAll();
doctorDao.insertInTx(doctors);
logger.debug("Doctors are synced successfully to the database");
logger.info(doctors.size()+" doctors have been added to database");
}
});
return observable;
}
Second Observable for getting patients list from my webservice
public Observable<List<Patients>> SyncPatients(){
Observable<List<Patients>> observable = MyWebService.getInterface().GetAllPatients();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<List<Patients>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Patients> patients) {
if(patients.size() == 0){
logger.debug("No Coming Patients");
return;
}
PatientDao PatientDao = MyApplication.getDaoSession().getPatientDao();
patientDao.deleteAll();
PatientDao.insertInTx(Patients);
logger.debug("Patients are synced successfully to the database");
logger.info(Patients.size()+" Patients have been added to database");
}
});
return observable;
}
Now i want to sync both doctors and patients lists and after both syncs finish, i want to show it on the screen of the tablet:
I have function called SyncAll
public void SyncAll(){
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
Observable.concat(doctorsObservable, patientsObservable)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
// Here the code to show on ListView
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
logger.debug("On SyncAll Next!!!");
}
});
}
onNext functions I save the list of doctors and list of patients to the database.
now when I call SyncDoctors() alone, it works
also when I call SyncPatients() alone, it works as well.
when I call SyncAll() the Doctors and Patients are not being saved to the database.
The Question is why the SyncDoctors() and SyncPatients() observables' onNext functions are called when I call SyncAll() !!
It is because you activate chain by calling .subscribe() in
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
You first create observable, then subscribe to it of SyncDoctors() and SyncPatients();
After that you return this Observable, but web response is triggered upon observable creation.
To solve that use .map():
public Observable<List<Doctor>> SyncDoctors(){
final Observable<List<Doctor>> observable = MyWebService.getInterface().GetAllDoctors();
observable.observeOn(Schedulers.io())
// in your code you performed db io on main thread, here it is fixed
.subscribeOn(Schedulers.io())
.map(new Func1<List<Doctor>, List<Doctor>>() {
#Override
public List<Doctor> call(List<Doctor> doctors) {
if(doctors.size() == 0){
logger.debug("No Coming Doctors");
return;
}
DoctorDao doctorDao = MyApplication.getDaoSession().getDoctorDao();
doctorDao.deleteAll();
doctorDao.insertInTx(doctors);
logger.debug("Doctors are synced successfully to the database");
logger.info(doctors.size()+" doctors have been added to database");
return doctors;
}
})
.observeOn(AndroidSchedulers.mainThread());
// Notice: use Observable.defer() or you'll get the same result all the tim
return Observable.defer(new Func0<Observable<List<Doctor>>>() {
#Override
public Observable<List<Doctor>> call() {
return observable;
}
});
}
You should not use .concat(), because it executes chain elements consequently. use .zip().first() intead.
There is also one issue: you perform db operations on main thread.
move chain to main thread after db update
Version with .zip:
void syncAll(){
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
Observable.zip(doctorsObservable, patientsObservable, new Func2<List<Doctor>, List<Patient>, Boolean>() {
#Override
public Boolean call(List<Doctor> doctors, List<Patient> patients) {
return true;
}
})
.first()
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
logger.debug("On SyncAll Next!!!");
}
});
}