I am trying to fetch list from an api and display in recyclerview using Retrofit with RxJava.
I have used below code-
ApiInterface apiService =
ApiService.getClient().create(ApiInterface.class);
Observable<MoviesResponse> call = apiService.getTopRatedMovies(API_KEY);
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
call.subscribe(new Observer<MoviesResponse>() {
#Override
public void onSubscribe(Disposable d) {
Toast.makeText(getApplicationContext(), d.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onNext(MoviesResponse moviesResponse) {
movies=moviesResponse;
moviesAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onComplete() {
Toast.makeText(getApplicationContext(), "complete", Toast.LENGTH_LONG).show();
}
});
Below two lines specify that the REST call will be made in a new thread . And when the call response returns, the onNext, onError, and onComplete methods are called on the mainThread.
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
But I am getting NetworkOnMainThreadException which is thrown when an application attempts to perform a networking operation on its main thread.Why am i getting this exception and how can i resolve this?
That's because you are subscribing to the observable 2 times. You can remove subscribe method from here:
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
So it will look like this:
ApiInterface apiService =
ApiService.getClient().create(ApiInterface.class);
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onSubscribe(Disposable d) {
Toast.makeText(getApplicationContext(), d.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onNext(MoviesResponse moviesResponse) {
movies=moviesResponse;
moviesAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onComplete() {
Toast.makeText(getApplicationContext(), "complete", Toast.LENGTH_LONG).show();
}
});
You are subscribing twice, the second time to the unmodified call source. You should have something like this:
ApiInterface apiService =
ApiService.getClient().create(ApiInterface.class);
Observable<MoviesResponse> call = apiService.getTopRatedMovies(API_KEY);
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onSubscribe(Disposable d) {
Toast.makeText(getApplicationContext(), d.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onNext(MoviesResponse moviesResponse) {
movies=moviesResponse;
moviesAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onComplete() {
Toast.makeText(getApplicationContext(), "complete", Toast.LENGTH_LONG).show();
}
});
Related
I created an Observable using the Observable.fromCallable method and subscribed to it as shown in the code snippet below.
Observable<String> stringObservable = Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(1000);
return Thread.currentThread().getName();
}
});
stringObservable.subscribeOn(Schedulers.io());
stringObservable.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String aDouble) {
Toast.makeText(SimpleActivity.this, "onNext: " + aDouble,
Toast.LENGTH_LONG).show();
}
#Override
public void onError(Throwable e) {
new AlertDialog.Builder(SimpleActivity.this)
.setTitle("Error")
.setMessage(e.toString())
.show();
}
#Override
public void onComplete() {
}
});
The snippet above produced a toast showing that the Callable was run on the main thread instead of the Schedulers.io thread. What's happening?
Every operation on an Observable creates a new instance and does not effect the original one. Therefore
stringObservable.subscribeOn(Schedulers.io());
does not affect your code below.
The correct way of using them would be in a chain instead of using a variable.
Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(1000);
return Thread.currentThread().getName();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String aDouble) {
Toast.makeText(SimpleActivity.this, "onNext: " + aDouble,
Toast.LENGTH_LONG).show();
}
#Override
public void onError(Throwable e) {
new AlertDialog.Builder(SimpleActivity.this)
.setTitle("Error")
.setMessage(e.toString())
.show();
}
#Override
public void onComplete() {
}
});
I use retrofit2 in my project. I have interface:
public interface ProductService {
#POST("findProducts")
Observable<ProductsResponse> getProducts();
}
and service method:
public ProductService getProductService() {
return getService(ProductService.class);
}
After that I use this service in my fragment. When I click to button I call this method:
#Override
public void onClick(View view) {
if (view.getId() == R.id.button) {
RestApiFactory.getInstance().getProductService().getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ProductHandler());
}
}
and realise handler in this fragment:
private class ProductHandler implements Observer<ProductsResponse> {
#Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe: start");
progressBar.setVisibility(View.VISIBLE);
}
#Override
public void onNext(ProductsResponse value) {
//save to DB in new Thread
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError: "+ e.getMessage());
price.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.INVISIBLE);
}
#Override
public void onComplete() {
Log.d(TAG, "onComplete: finish");
price.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.INVISIBLE);
}
}
is it correct to implement ProductHandler implements Observer<ProductsResponse>'s methods
onSubscribe
onNext
onError
onComplete
in fragment?
I'm advised to move this in a singleton:
MySingleton.getInstanse().getData();
and move all to getData :
public void getData(){
RestApiFactory.getInstance().getProductService().getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ProductsResponse>() {
#Override
public void onSubscribe(Disposable d) {
//send broadcast and catch in Fragment
}
#Override
public void onNext(ProductsResponse productsResponse) {
//send broadcast and catch in Fragment
}
#Override
public void onError(Throwable e) {
//send broadcast and catch in Fragment
}
#Override
public void onComplete() {
//send broadcast and catch in Fragment
}
});
}
but I think this is nonsense. who will tell you how to use it (in what place) Observable from retrofit?
there is no need to implement Observer.You can just need to create an observer for the same.Let's see the example.
service.getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(mObserver);
}
the observer should look like and must be initialized in oncreate:
mObserver = new Observer<ProductsResponse>() {
#Override
public void onSubscribe(Disposable d) {
mDisposableList.add(d);
}
#Override
public void onNext(ProductsResponse value) {
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
};
I've an Observable something like this:
#GET("endpoint")
Observable<Something> getSomething();
and Subscriber like this
Subscriber<Something> somethingSubscriber = new Subscriber<Something>() {
public void onCompleted() {
}
public void onError(Throwable e) {
//handle exceptions
}
public void onNext() {
//do something
}
In my OnClickListener associated with a button, i make a subscription
getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(somethingSubscriber);
If i don't have an internet connection, onError is called and i do some exception handling. when I press the button again (assume i want to retry), the callback methods do not get called.
I want that onNext / onError callbacks get called everytime I press the button.
There is extention for RxJava. It has a lot of "cool tools", but for handling retrofit errors you can use ResponseOrError class.
So in you case it would looks like:
final PublishSubject<Object> clickSubject = PublishSubject.create();
final Observable<ResponseOrError<Something>> responseOrErrorObservable = clickSubject
.flatMap(new Func1<Object, Observable<ResponseOrError<Something>>>() {
#Override
public Observable<ResponseOrError<Something>> call(Object o) {
return getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.compose(ResponseOrError.<Something>toResponseOrErrorObservable());
}
})
.replay(1)
.refCount();
final Observable<Throwable> error = responseOrErrorObservable
.compose(ResponseOrError.<Something>onlyError())
.subscribe(new Action1<Segment>() {
#Override
public void call(Throwable throwable) {
// what to do on error, some toast or what ever yu need
}
});
final Observable<UserInfoResponse> success = responseOrErrorObservable
.compose(ResponseOrError.<Something>onlySuccess())
.subscribe(new Action1<Something>() {
#Override
public void call(Something some) {
// code what to do on success
}
});
And now, into onClick you just need to put clickSubject.onNext(null)
.replay(1).refCount(); needed because there are 2 Observables that uses responseOrErrorObservable, so without it retrofit request will "happens" two times.
You are reusing the same Subscriber. Once you get the onError or a result (so it completes) the subscriber is unsubscribed. Try to pass every time a new subscriber.
use this code
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Something>() {
#Override
public void call(Something something) {
//do something
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//handle exceptions
}
},
new Action0() {
#Override
public void call() {
}
});
}
});
Addition
or
replace this
Subscriber<Something> somethingSubscriber = new Subscriber<Something>() {
public void onCompleted() {
}
public void onError(Throwable e) {
//handle exceptions
}
public void onNext() {
//do something
}
};
to
Subscriber<String> somethingSubscriber = new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
};
In my Case onNext() and onError() methods are not getting called because of my model class wrong parsing, I was taking a double object as Integer so NumberFormatException was thrown and nothing was happening after getting the result from retrofit.
this below code work fine on my activity or fragments, but when i rotate my phone this method work again, how can i move that to service and get event on Ui?
private void getUserStories() {
mSubscriptions.add(mDataManager.getUserPosts(mUser)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(mDataManager.getScheduler())
.subscribe(new Subscriber<Post>() {
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) {
hideLoadingViews();
Timber.e("There was a problem loading the user stories " + e);
DialogFactory.createSimpleOkErrorDialog(
getActivity(),
getString(R.string.error_stories)
).show();
}
#Override
public void onNext(Post story) {
hideLoadingViews();
mPostAdapter.addItem(story);
}
}));
}
I have the following code. It's basically an attempt to send all data from a specific SQLite table to DynamoDB:
Observable.create(new Observable.OnSubscribe<Area>() {
#Override
public void call(Subscriber<? super Area> subscriber) {
try {
for (Area item : areaDao.listAll()) {
subscriber.onNext(item);
}
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
}).flatMap(new Func1<Area, Observable<Area>>() {
#Override
public Observable<Area> call(Area area) {
dynamoDBMapper.save(area);
return Observable.just(area);
}
}).observeOn(
AndroidSchedulers.mainThread()
).doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.w("AreaHandler", "Could not upload area", throwable);
}
}).doOnCompleted(new Action0() {
#Override
public void call() {
Toast.makeText(ctx, R.string.toast_upload_successful, Toast.LENGTH_SHORT).show();
}
}).subscribeOn(
Schedulers.io()
).subscribe(new Action1<Area>() {
#Override
public void call(Area area) {
areaDao.delete(area.getId());
}
});
I'm trying to run it on an emulator with disabled Internet connectivity, and what happens is that the Dynamo client does a couple of (failed) retries, then an exception is thrown and it crashes the app. From what I read in the docs, the exception should be swallowed by doOnError instead of being let out to the wild and killing the process.
What am I missing?
You are grabbing the error in the wrong place. doOnError is for side effects. It does not handle the error.
Option 1. Pass in two Action1
Observable.just(1, 2, 3)
.subscribe(
new Action1<Integer>() {
#Override
public void call(Integer integer) {
System.out.println(integer);
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
System.err.print(throwable);
}
});
Option 2: Pass in an Observer
Observable.just(1, 2, 3)
.subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
System.err.print(throwable);
}
#Override
public void onNext(Integer integer) {
System.out.println(integer);
}
});