I use RxJava + Retrofit to make API calls in my Android app. There may be cases when user makes a request and his token is expired. In this cases I receive a normal response in my onNext, but the response contains not the result but an error element with some code. If such thing happens I need to re-login the user and only after getting a new token repeat the original request.
So I want to organize this using RxJava.
To make things easier I will bring a simple example. Let's say I have the following method:
public void test(int someInt){
Observable.just(someInt)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
log("onCompleted");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
log("onError");
}
#Override
public void onNext(Integer integer) {
log("onNext - " + integer);
}
});
I want to check if (someInt == 0) before onNext() is called. If I get false I want to continue and get onNext() called, but if I get true I want to perform some action and repeat the original observable only once, if the condition returns false second time I don't want to repeat again.
Can someone help me to figure out what options do I have for this?
P.S. I am new in RX world.
Here you go. Since you want to retry the whole chain .retryWhen is great for it so you have to "play" a bit with the errors.
Below if you detect a invalid token, you pass an error (only on the first time) which the retryWhen will catch and resubscribe to the whole rx chain (starting from Observable.just(someInt)).
haveRetriedOnce = false;
Observable.just(someInt)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(integer ->{
if(integer == 0){
if(haveRetriedOnce){
return Observable.error(new UserOperationException());
}
// problem, throw an error and the .retryWhen will catch it
return Observable.error(new InvalidTokenException());
}else{
return Observable.just(integer);
}
})
.retryWhen(observable -> observable.flatMap(throwable->{
if(throwable instanceOf InvalidTokenException){
haveRetriedOnce = true;
return just(0); // retry, the int here is irrelevant
}else{
// other error, pass it further
return Observable.error(throwable);
}
}))
.subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
log("onCompleted");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
log("onError");
}
#Override
public void onNext(Integer integer) {
log("onNext - " + integer);
}
}
Related
My Reactive knowledge is very basic and I was wondering what the right way would be if I like to return an observable from a function which is using an observable. I wanna extend the observable which I am calling with a check.
In my example, I think it is a lot of code for not much. I think I would also need to worry about the disposable of the inner observable. Do I?
public Completable updateUserPhotoURL(Uri photoURL, UserProfileChangeRequest profileUpdates) {
return Completable.create(emitter -> {
if (mFirebaseUser == null) {
emitter.onError(new Exception("Firebase User is not initiated"));
}
RxFirebaseUser.updateProfile(mFirebaseUser, profileUpdates).complete()
.subscribe(new DisposableCompletableObserver() {
#Override
public void onComplete() {
emitter.onComplete();
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
emitter.onError(e);
}
});
});
}
What would be the right (more elegant) way of doing so?
I have a ChatThreadsActivity() which displays messages between two people.
Whenever the user opens the activity, I make an API to fetch the most recent 100 messages. So in the onCreate() method I call makeApiRequestToGetChatThread().
I know I am doing alot of things wrong here.
Inside onNext() I add rows to the chatThreadAdapter. I know this is wrong since I update adapter after ever insert.
chatDaoObject.queryChatThreadsFromDB(someId).observeOn(AndroidSchedulers.mainThread()).subscribe()
Should this be anonymously called? When will this be unsubscribed if ever?
How do I unsubscribe from it?
I've read about BackPressure and realize this is it, I've run this on Android Monitor and here's how I can identify it. Am I doing this right?
private void makeApiRequestToGetChatThreads() {
public void onResponse(Call call, final Response response) {
final String responseString = response.body().string();
runOnUiThread (() -> {
final JSONArray array = new JSONArray(responseString);
JSONObject obj;
for (int i=0 ; i < array.length ; i++) {
obj = new JSONObject(array.get(i));
insertAChatIntoDB (obj);
}
}
});
}
private void insertAChatIntoDB(JSONObject o) {
if(insertSubscriber != null) {
insertSubscriber.unsubscribe();
}
insertSubscriber = new Subscriber<Long>() {
public void onCompleted() {
}
public void onError() {
}
public void onNext() {
chatDaoObject.queryChatThreadsFromDB(someId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<ChatObject>>() {
#Override
public void call(List<ChatObject> chatList) {
if (chatList != null) {
//Royal Screw up
//After every insertion in the DB I update the UI
chatThreadAdapter.addAllChatThreadsIntoList(chatList);
//Notify is called inside the above below
//notifyItemRangeChanged(initialPosition,chatList.size())
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
}
});
}
};
try {
//Inserts happening on Schedulers.computation()
chatDaoObject.insertAChatInDB(o).observeOn(Schedulers.computation()).subscribe(insertSub);
} catch (JSONException e) {
e.printStackTrace();
Crashlytics.logException(e);
}
}
I use RxJAVA and SQLBrite Dao and here's what the query looks like:
//In `ChatDao` class
ChatDataDao extends Dao {
...
...
public Observable<long> insertAChatInDB(JSONObject o) {
ChatThreadsTable.ContentValuesBuilder valuesBuilder = ChatThreadsTable.contentValues();
...
//populating columns values
...
return insert(ChatThreadsTable.TABLE_NAME, valuesBuilder.build(), SQLiteDatabase.CONFLICT_IGNORE);
}
public Observable<List> queryChatThreadsFromDB () {
return rawQuery("SELECT * FROM " + ChatThreadsTable.TABLE_NAME).run().mapToList(ChatObjectMapper.MAPPER);
}
...
...
}
Edit:
Is this the right way to query db without worrying about subscription/unsubscription?
rawQuery("SELECT * FROM " + SomeObject.TABLE_NAME + " ORDER BY " + SomeObject.COL1 + " , " +SomeObject.COL2 + " DESC").run().mapToList(SomeObjectMapper.MAPPER)
.flatMap(new Func1<List<SomeObject>, Observable<SomeObject>>() {
#Override
public Observable<SomeObject> call(List<SomeObject> SomeObjects) {
return Observable.from(SomeObjects);
}
}).doOnNext(new Action1<SomeObject>() {
#Override
public void call(SomeObject chatThreadObject) {
}
}).subscribe();
what is going on with all these tutorials telling people to create their own Subscribers? Here's a cleaned up version:
private void makeApiRequestToGetChatThreads() {
// ... some call that calls back onResponse
}
public void onResponse(Call call, final Response response) {
// are you sure this is how you parse a JSON String?
Observable
.from(response.body().string())
.observeOn(Schedulers.computation())
.flatMapIterable(JsonArray::new)
.map(JSONObject::new)
.flatMap(chatDaoObject::insertAChatInDB)
.flatMap(chatDaoObject::queryChatThreadsFromDB)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(chatThreadAdapter::addAllChatThreadsIntoList)
.subscribe(dummy -> {}, throwable -> {
e.printStackTrace();
Crashlytics.logException(e);
});
}
It's quite likely that there's a better way to directly make the network call return an Observable. Check your documentation / peers.
When the Observable completes all processing and UI updates, it will unsubscribe on it's own. However, if the network call takes a while to complete, the user might have already switched screens/apps, and the UI manipulation will break your app. Consider saving the subscription in your view and unsubscribing. Hint: creating the subscription in the onResponse is... not optimal.
You are getting 100s of threads, and updating the UI for each and every one of them. Are you sure you want that?
I don't think you need to care about backpressure.
I am learning RxJava and am testing a scenario where I read data from a DB and then post it to a Queue. I just made a sample mock of the whole process but I don't seem to find the Observable working as I wanted it to ie. asynchronously.
This is my code:
package rxJava;
import java.util.ArrayList;
import java.util.List;
import rx.Observable;
import rx.Observer;
import rx.functions.Action1;
public class TestClass {
public static void main(String[] args) {
TestClass test = new TestClass();
System.out.println("---START---");
test.getFromDB().subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.out.println("Publish complete.");
}
#Override
public void onError(Throwable t) {
System.out.println(t.getMessage());
}
#Override
public void onNext(String s) {
test.publishToQueue(s).subscribe(new Observer<Boolean>() {
#Override
public void onNext(Boolean b) {
if (b) {
System.out.println("Successfully published.");
}
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable arg0) {
}
});
};
});
System.out.println("---END---");
}
public Observable<String> getFromDB() {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 30; i++) {
list.add(Integer.toString(i));
}
return Observable.from(list).doOnNext(new Action1<String>() {
#Override
public void call(String temp) {
if (temp.contains("2")) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
public Observable<Boolean> publishToQueue(String s) {
return Observable.defer(() -> {
try {
if (s.contains("7")) {
Thread.sleep(700);
}
System.out.println("Published:: " + s);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Observable.just(true);
});
}
}
Suppose I get a list from the DB asynchronously and want to post it to the queue,. I have used an Observable returned from getFromDB and have subscribed to it which mimics the data I get from DB. Every time I get the data from DB , I want to push it to a queue using publishToQueue which also returns an Observable. I wanted to make the queue call also asynchronous. Now on positive acknowledgement from the queue such as the Boolean which I am returning (Observable<Boolean>), I want to print something.
So basically I just want both the processes to be asynchronous. For every data from DB, I push it to the Queue asynchronously.
I have added Thread.sleep() in both the methods, db call and queue so as to mimic a delay and to test the asynchronous operations. I think this is what causing the problem. But I also tried Obseravable.delay() but that doesn't even produce any output.
Please help me understand how this works and how I can make it work as I want it to.
You have to specified subscribeOn value.
Observable.just("one", "two", "three", "four", "five")
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* an Observer */);
http://reactivex.io/documentation/operators/subscribeon.html
By default, RxJava is synchronous. It means that everything will be perform in the same thread (and the current thread), by default. You can perform tasks in another thread thanks to observeOn / subscribeOn methods, or using some operators that perform tasks in another job (because it use another scheduler, like delay, interval, ...)
In your example, you have to explitly set in which scheduler the subscription will pe performed. (here, in which thread Observable.from will emit your list)
test.getFromDb()
.subscribeOn(Schedulers.io())
.subscribe();
Then you can use the flatMap operator and calling your publishToQueue method. This method will be executed in the previous scheduler, but you can force it to use another scheduler, thanks to observeOn method. Everything after the observeOn method will be executed in another thread.
test.fromDb()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.flatMap(l -> test.publishToqueue(l))
.subscribe();
I use Retrofit with RxJava Observables and lambda expressions. I'm new to RxJava and cannot find out how to do the following:
Observable<ResponseBody> res = api.getXyz();
res.subscribe(response -> {
// I don't need the response here
}, error -> {
// I might be able to handle an error here. If so, it shall not go to the second error handler.
});
res.subscribe(response -> {
// This is where I want to process the response
}, error -> {
// This error handler shall only be invoked if the first error handler was not able to handle the error.
});
I looked at the error handling operators, but I don't understand how they can help me with my usecase.
Method 1: Keep the two Subscribers but cache the Observable.
Just keep everything as it is now, but change the first line to:
Observable<ResponseBody> res = api.getXyz().cache();
The cache will make sure that the request is only sent once but that sill both Subscribers get all the same events.
This way whether and how you handle the error in the first Subscriber does not affect what the second Subscriber sees.
Method 2: Catch some errors with onErrorResumeNext but forward all others.
Add onErrorResumeNext to your Observable to produce something like this (in the "inner" object):
Observable observable = Observable.error(new IllegalStateException())
.onErrorResumeNext(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(Throwable throwable) {
if (throwable instanceof NumberFormatException) {
System.out.println("NFE - handled");
return Observable.empty();
} else {
System.out.println("Some other exception - panic!");
return Observable.error(throwable);
}
}
});
And only subscribe once (in the "outer" object):
observable.subscribe(new Subscriber() {
#Override
public void onCompleted() {
System.out.println("onCompleted");
}
#Override
public void onError(Throwable e) {
System.out.println("onError");
e.printStackTrace();
}
#Override
public void onNext(Object o) {
System.out.println(String.format("onNext: %s", String.valueOf(o)));
}
});
This way, the error is only forwarded if it cannot be handled in the onErrorResumeNext - if it can, the Subscriber will only get a call to onCompleted and nothing else.
Having side effects in onErrorResumeNext makes me a bit uncomfortable, though. :-)
EDIT: Oh, and if you want to be extra strict, you could use Method 3: Wrap every case in a new object.
public abstract class ResultOrError<T> {
}
public final class Result<T> extends ResultOrError<T> {
public final T result;
public Result(T result) {
this.result = result;
}
}
public final class HandledError<T> extends ResultOrError<T> {
public final Throwable throwable;
public Result(Throwable throwable) {
this.throwable = throwable;
}
}
public final class UnhandledError<T> extends ResultOrError<T> {
public final Throwable throwable;
public Result(Throwable throwable) {
this.throwable = throwable;
}
}
And then:
Wrap proper results in Result (using map)
Wrap handle-able errors in HandledError and
un-handle-able errors in UnhandledError (using onErrorResumeNext with an if clause)
handle the HandledErrors (using doOnError)
have a Subscriber<ResultOrError<ResponseBody>> - it will get notifications (onNext) for all three types but will just ignore the HandledErrors and handle the other two types.
I'm using Retrofit with RxJava in an Android app for communications and have to handle error on parsing the response from a seemly ok HTTP response (status 200 code).
I have also implemented a way of handling the error using retryWhen operator which is connected to user's input to decide whether to retry it or not. This works by resubscribing to the original Observable.
The first approach I have tried was to have something like this:
services.getSomething()
.map(response -> {
if (checkBadResponse(response)) {
throw new RuntimeException("Error on service");
} else {
return parseResponse(response);
}
}).retryWhen(this::shouldRetry);
With this the service is not called again. It seems the retryWhen operator cannot resubscribe to the service's Observable.
What end up working was implementing another operator which doesn't send the onCompleted forward and use it with lift like the following:
public class CheckResponseStatus<T> implements Observable.Operator<ResponsePayload<T>, ResponsePayload<T>> {
#Override
public Subscriber<? super ResponsePayload<T>> call(Subscriber<? super ResponsePayload<T>> subscriber) {
return new Subscriber<ResponsePayload<T>>() {
private boolean hasError = false;
#Override
public void onCompleted() {
if (!hasError)
subscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
hasError = true;
subscriber.onError(e);
}
#Override
public void onNext(ResponsePayload<T> response) {
if (response.isOk()) {
subscriber.onNext(response);
} else {
hasError = true;
subscriber.onError(new RuntimeException(response.getMessage()));
}
}
};
}
}
Using it like:
services.getSomething()
.lift(new CheckResponseStatus())
.map(response -> parseResponse(response))
.retryWhen(this::shouldRetry);
Is this the correct way of dealing with it or is there a simpler, better way?
It's looks like a bug in rx-java implementation. Anyway, throwing an exception from map function is a bad thing since the function is supposed to be pure (e.g. without side effects). You should use a flatMap operator in your case:
services.getSomething()
.flatMap(response -> {
if (checkBadResponse(response)) {
return Observable.<ResponseType>error(new RuntimeException("Error on service"));
} else {
return Observable.<ResponseType>just(parseResponse(response);
}
}).retryWhen(this::shouldRetry);
The code above works as expected and really retries the request if error occurs.