Using RX-Java for dependent actions and http requests - android

I'm trying to use rx-java on Android to do few sequential http requests, each of which is dependent of the response of the former one.
This does not quite fit the map() / doFinall() model and so I'm not sure what would be the best way to do this without getting into "callback hell" as well as writing concise code.
More concretely:
do http GET "/x"
do http GET "/y" if (2) was successfully
do calculation on the result of GET /y
Any suggestions on how to go about this?

I think flatMap is what you're looking for. For example, assuming you have the following methods:
Observable<Foo> getFoo();
Observable<Bar> getBar(Foo foo); //needs a Foo first
You could effectively chain them this way:
getFoo().flatMap(new Func1<Foo, Observable<Bar>>() {
#Override
public Observable<Bar> call(Foo foo) {
return getBar(foo);
}
});
You could then perform some calculation with the final result Bar by subscribing to the resulting Observable<Bar> (full example shown for clarity):
getFoo().flatMap(new Func1<Foo, Observable<Bar>>() {
#Override
public Observable<Bar> call(Foo foo) {
return getBar(foo);
}
}).subscribe(new Action1<Bar>() {
#Override
public void call(Bar bar) {
//everything succeeded, so perform calculation to the Bar
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//handle an error that occurred anywhere in the chain
}
});
Note that an error anywhere in the process of getting the Foo or the Bar will be handled by the Action1 that we provide when subscribing to the Observable. It is, of course, painfully verbose because Java, but at least it avoids nesting Observables/callback hell.

Related

Managing different Exceptions from different rx.Observables

Following the topic discussed here. I'm coding an Android App using the Clean Architecture. I've an Interactor that takes care of retriving the User's feed data. The flow is like this:
I must fetch the Feed data from the a Repository which calls a Retrofit's service to do the API call.
If something goes wrong I've to fetch the feed data from a FeedCache that internally works with Sqlite.
I've to merge this feed collection with another bunch of feeds from another cache called PendingPostCache. This cache contains all the articles that the user couldn't post (because something went wrong, didn't had internet connection, etc.)
My FeedCache and PendingPostCache both work with Sqlite. Botch can throw DBExceptions if something went wrong. My FeedRepository the ones that makes the requests against the server-side can also throw exceptions if something goes wrong (ServerSideException).
Here's the whole code from my Interactor:
mFeedRepository.getFeed(offset, pageSize) //Get items from the server-side
.onErrorResumeNext(mFeedCache.getFeed(userSipid)) //If something goes wrong take it from cache
.mergeWith(mPendingPostCache.getAllPendingPostsAsFeedItems(user)) //Merge the response with the pending posts
.subscribe(new DefaultSubscriber<List<BaseFeedItem>>() {
#Override
public void onNext(List<BaseFeedItem> baseFeedItems) {
callback.onFeedFetched(baseFeedItems);
}
#Override
public void onError(Throwable e) {
if (e instanceof ServerSideException) {
//Handle the http error
} else if (e instanceof DBException) {
//Handle the database cache error
} else {
//Handle generic error
}
}
});
I don't like having those instanceof. I'm thinking on creating a custom subscriber, something called like MyAppSubscriber, which implements the onError method, makes those instanceof comparations, and execute some methods called onServerSideError(), onDBError(). That way the code is going te be a lot cleaner and I can spare writing that instanceof boilerplate code. Has someone a better idea about how to approach this issue? Some way to avoid the custom Subscriber?
Just use composition:
public <T,E> Function<Throwable, Observable<T>> whenExceptionIs(Class<E> what, Function<E, Observable<T>> handler) {
return t -> {
return what.isInstance(t) ? handler.apply(what.cast(t)) : Observable.error(t);
};
}
Then you use it normally :
Observable.from(...).flatMap(...)
.onErrorResumeNext(whenExceptionIs(ServerSideException.class, e-> Observable.empty()))
.onErrorResumeNext(whenExceptionIs(DBException.class, e-> ...))
You can even abstract all that in one method:
public <T> Transformer<T, T> errorHandling() {
return src -> src
.onErrorResumeNext(whenExceptionIs(ServerSideException.class, e-> Observable.empty()))
.onErrorResumeNext(whenExceptionIs(DBException.class, e-> ...));
}
Observable.from(...).flatMap(...)
.compose(errorHandling())
.subscribe();

How to execute different retrofit requests inside RxJava's switchMap() operator?

I have a searchBar (an EditText) with four tabs below it (each tab should display different results). I'm using RxJava with RxBinding to listen and react to text changes events, and I'm using switchMap() operator to execute a Retrofit service for each text change emission.
Since user can select any of the four tabs I actually execute the corresponding Retrofit request for that tab.
For each of those Retrofit services I receive a different response object.
How can I handle different return types inside switchMap() since the last one needs a common type for all?
I have already asked a similar question previously but the answer while it works doesn't lets me to consume the data from my subscriber. Or is my approach wrong from the beginning and I should try a different approach ?
Code :
RxTextView.textChangeEvents(searchbar.getEditText())
.debounce(400, TimeUnit.MILLISECONDS)
.filter(new Func1<TextViewTextChangeEvent, Boolean>() {
#Override
public Boolean call(TextViewTextChangeEvent text) {
return (text.text().length() > 2);
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(new Func1<TextViewTextChangeEvent, Observable<Void>>() {
#Override
public Observable<Void> call(TextViewTextChangeEvent textViewTextChangeEvent) {
String searchBarText = textViewTextChangeEvent.text().toString();
switch (visibleTab) {
case TAGS:
presenter.executeSearchPostsByTag(searchBarText, String.valueOf(0));
case PEOPLE:
return presenter.executeSearchPostsByPeople(searchBarText, String.valueOf(0));
case COMPANIES:
return presenter.executeSearchPostsByCompanies(searchBarText, String.valueOf(0));
case JOBS:
return presenter.executeSearchPostsByJobs(searchBarText, String.valueOf(0));
default:
return presenter.executeSearchPostsByTag(searchBarText, String.valueOf(0));
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Void>() {
#Override
public void onCompleted() {
Timber.i("ON COMPLETED");
}
#Override
public void onError(Throwable e) {
Timber.i("ON ERROR e : %s", e.getMessage());
}
#Override
public void onNext(Void aVoid) {
Timber.i("ON NEXT");
}
});
In the code above you 'll see that I have return type of Observable but that doesn't works I just added it so you 'll see what I'm doing.
Thing is, do any of the executeSearchPostsBy* methods return a non-empty Observable? If all of their Observables are empty, then you can just tack on .cast(Void.class) to all of them. If they do return non-empty observables but you don't care about the items, then tack on .ignoreElements().cast(Void.class).
If you need to do some processing for anything that is returned, then you should do that in different methods, in their own Observable chains.
If you need to do some processing that is common to all of them, then you need to adjust your model to reflect this, even if it's just wrapper classes.

Get one result from 2 observables with RxJava

I am new at RxJava and I have some pain to execute my first 'difficult' query.
I have two Observables generated from Retrofit, one that 'ping' a new api, the other the old one. The first one will query 'http://myurl.com/newapi/ping', the second one 'http://myurl.com/oldapi/ping'. Result from this request doesn't matter, I just want to know if the server is using the new or old api.
So I would like to call both observables at the same time, and finally have a boolean at the end to know if I'm using old or new api.
I tried something like that
Observable.mergeDelayError(obsOldApi,obsNewApi)
.observeOn(AndroidSchedulers.mainThread(), true)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
});
But onError will be called once (I would like it to be called only if both request failed) and when onNext is called, I don't know from which request it came (old or new api ?).
Thank you for you help
For simplicity, let say that you'll received "NEW" or "OLD" regarding which api is available.
The difficulty of your operation is to manage errors : RxJava deals errors as terminal state. So you'll have to ignore this error, using .onErrorResumeNext() for example.
Observable<String> theOld = oldApi.map(r -> "OLD")
// ignore errors
.onErrorResumeNext(Obervable.empty());
Observable<String> theNew = newApi.map(r -> "NEW")
.onErrorResumeNext(Obervable.empty());
Observable.merge(theOld, theNew)
.first() // if both api are in errors
.subscribe(api -> System.out.println("Available API : "+api));
I added the operator first : it will take only the first result ("OLD" or "NEW") but trigger an error if the previous Observable is empty, which is the case if both API are unavaible.

State mutation in reactive way

Recently I started android project with hard usage of Reactive extensions. I've read some introductions and tutorials, but I'm still at beginner's level. According to this article:
everything is a stream
however my current understanding (or mental barrier) tells me that any operation which mutates state (removing data from repository for example) should not be/return a stream/observable.
Little background about my domain: I have a use case for registering geofences. Since geofences do not survive reboot, I keep track of active geofences in repository. Sometimes app needs to remove geofence, so basic steps to achieve this are:
retrieve geofence from repository
remove geofence from device
remove geofence from repository
my current solution is following:
geofenceRepository.get(id)
.map(new Func1<Geofence, String>() {
#Override
public String call(Geofence geofence) {
geofenceRepository.delete(geofence.getId()); // synchronous call here
return geofence.getRequestId();
}
})
.toList()
.flatMap(new Func1<List<String>, Observable<Status>>() {
#Override
public Observable<Status> call(List<String> ids) {
return locationProvider.removeGeofences(ids);
}
});
where Geofence is my custom data structure and locationProvider is from this nice library.
You'll notice that data retrieval is implemented as stream/observable unlike delete.
What I don't like in above example is: map operator with side effect
Questions
What would be better solution to be more "reactive", what I'm missing here?
Does it make sense to use reactive approach at all?
by reactive programming I mean:
programming with asynchronous data streams
I don't see any problem with your approach and being more reactive would mean more API uses/returns Observables. You can have side-effects in any of the lambdas but be careful when you mutate a value since if asynchrony is involved, the same object may be mutated at the same time at different stages of the pipeline. Usually, we use immutable or effectively immutable values to avoid this problem. There is no real need to split your activities so the suggested doOnNext separation is a preference of the particular developer
If your geofenceRepository.delete had a version that returns an Observable of some sort, you could go more reactive by flatMapping over it:
get(id)
.flatMap(f -> geoFence.deleteAsync(f.getId()).map(v -> f.getRequestId()))
.toList()
.flatMap(...)
.subscribe(...)
Here, deleteAsync would return an Observable<Void> which when completes, will resume the main sequence with the requestId.
Reactive is great and I think this situation is perfect.
I think what you really want to do here is make sure each of your operators does exactly 1 thing. Like you said, the flatMap is also removing your geofence.
Try using the onNext operator in your chain for the removal. What you want to do it retrieve it, which it looks like geofenceRepository.get(id), remove it with an operator, then remove it from the locationProvider. Maybe something like:
geofenceRepository.get(id)
.map(new Func1<Geofence, String>() {
#Override
public String call(Geofence geofence) {
return geofence.getRequestId();
}
})
.doOnNext(new Action1<String>){
#Override
public void call(final String geoFenceId) {
geofenceRepository.delete(geofence.getId());
}
})
.doOnNext(new Action1<String>() {
#Override
public void call(final String geoFenceId) {
return locationProvider.removeGeofences(ids);
}
});
What you probably really want to do is create two subscribers. That way if you want to watch the status of one or both you can. You could combine the status of each. It depends a bit on if deleting from the repository and deleting from the provider are independent.
Observable<String> getFence = geofenceRepository.get(id)
.map(new Func1<Geofence, String>() {
#Override
public String call(Geofence geofence) {
return geofence.getRequestId();
}
});
getFence.subscribe(new Action1<String>){
#Override
public void call(final String geoFenceId) {
geofenceRepository.delete(geofence.getId());
}
});
getFence.map(new Func1<String, Status>() {
#Override
public Status call(final String geoFenceId) {
return locationProvider.removeGeofences(ids);
}
}).subscribe(new Action1<Status>(){
#Override
public void call(final Status status(){
//Handle your status for each removal
}
});

Android: Polling a server with Retrofit

I'm building a 2 Player game on Android. The game works turnwise, so player 1 waits until player 2 made his input and vice versa. I have a webserver where I run an API with the Slim Framework. On the clients I use Retrofit. So on the clients I would like to poll my webserver (I know it's not the best approach) every X seconds to check whether there was an input from player 2 or not, if yes change UI (the gameboard).
Dealing with Retrofit I came across RxJava. My problem is to figure out whether I need to use RxJava or not? If yes, are there any really simple examples for polling with retrofit? (Since I send only a couple of key/value pairs) And if not how to do it with retrofit instead?
I found this thread here but it didn't help me too because I still don't know if I need Retrofit + RxJava at all, are there maybe easier ways?
Let's say the interface you defined for Retrofit contains a method like this:
public Observable<GameState> loadGameState(#Query("id") String gameId);
Retrofit methods can be defined in one of three ways:
1.) a simple synchronous one:
public GameState loadGameState(#Query("id") String gameId);
2.) one that take a Callback for asynchronous handling:
public void loadGameState(#Query("id") String gameId, Callback<GameState> callback);
3.) and the one that returns an rxjava Observable, see above. I think if you are going to use Retrofit in conjunction with rxjava it makes the most sense to use this version.
That way you could just use the Observable for a single request directly like this:
mApiService.loadGameState(mGameId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
#Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
});
If you want to repeatedly poll the server using you can provide the "pulse" using versions of timer() or interval():
Observable.timer(0, 2000, TimeUnit.MILLISECONDS)
.flatMap(mApiService.loadGameState(mGameId))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
#Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
}).
It is important to note that I am using flatMap here instead of map - that's because the return value of loadGameState(mGameId) is itself an Observable.
But the version you are using in your update should work too:
Observable.interval(2, TimeUnit.SECONDS, Schedulers.io())
.map(tick -> Api.ReceiveGameTurn())
.doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.retry()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(sub);
That is, if ReceiveGameTurn() is defined synchronously like my 1.) above, you would use map instead of flatMap.
In both cases the onNext of your Subscriber would be called every two seconds with the latest game state from the server. You can process them one after another of limit the emission to a single item by inserting take(1) before subscribe().
However, regarding the first version: A single network error would be first delivered to onError and then the Observable would stop emitting any more items, rendering your Subscriber useless and without input (remember, onError can only be called once). To work around this you could use any of the onError* methods of rxjava to "redirect" the failure to onNext.
For example:
Observable.timer(0, 2000, TimeUnit.MILLISECONDS)
.flatMap(new Func1<Long, Observable<GameState>>(){
#Override
public Observable<GameState> call(Long tick) {
return mApiService.loadGameState(mGameId)
.doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.onErrorResumeNext(new Func1<Throwable, Observable<GameState>(){
#Override
public Observable<GameState> call(Throwable throwable) {
return Observable.emtpy());
}
});
}
})
.filter(/* check if it is a valid new game state */)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
#Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
}).
This will every two seconds:
* use Retrofit to get the current game state from the server
* filter out invalid ones
* take the first valid one
* and the unsubscribe
In case of an error:
* it will print an error message in doOnNext
* and otherwise ignore the error: onErrorResumeNext will "consume" the onError-Event (i.e. your Subscriber's onError will not be called) and replaces it with nothing (Observable.empty()).
And, regarding the second version: In case of a network error retry would resubscribe to the interval immediately - and since interval emits the first Integer immediately upon subscription the next request would be sent immediately, too - and not after 3 seconds as you probably want...
Final note: Also, if your game state is quite large, you could also first just poll the server to ask whether a new state is available and only in case of a positive answer reload the new game state.
If you need more elaborate examples, please ask.
UPDATE: I've rewritten parts of this post and added more information in between.
UPDATE 2: I've added a full example of error handling with onErrorResumeNext.
Thank you, I finally made it in a similar way based the post I referred to in my question. Here's my code for now:
Subscriber sub = new Subscriber<Long>() {
#Override
public void onNext(Long _EmittedNumber)
{
GameTurn Turn = Api.ReceiveGameTurn(mGameInfo.GetGameID(), mGameInfo.GetPlayerOneID());
Log.d("Polling", "onNext: GameID - " + Turn.GetGameID());
}
#Override
public void onCompleted() {
Log.d("Polling", "Completed!");
}
#Override
public void onError(Throwable e) {
Log.d("Polling", "Error: " + e);
}
};
Observable.interval(3, TimeUnit.SECONDS, Schedulers.io())
// .map(tick -> Api.ReceiveGameTurn())
// .doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.retry()
.subscribe(sub);
The problem now is that I need to terminate emitting when I get a positive answer (a GameTurn). I read about the takeUntil method where I would need to pass another Observable which would emit something once which would trigger the termination of my polling. But I'm not sure how to implement this.
According to your solution, your API method returns an Observable like it is shown on the Retrofit website. Maybe this is the solution? So how would it work?
UPDATE:
I considered #david.miholas advices and tried his suggestion with retry and filter. Below you can find the code for the game initialization. The polling should work identically: Player1 starts a new game -> polls for opponent, Player2 joins the game -> server sends to Player1 opponent's ID -> polling terminated.
Subscriber sub = new Subscriber<String>() {
#Override
public void onNext(String _SearchOpponentResult) {}
#Override
public void onCompleted() {
Log.d("Polling", "Completed!");
}
#Override
public void onError(Throwable e) {
Log.d("Polling", "Error: " + e);
}
};
Observable.interval(3, TimeUnit.SECONDS, Schedulers.io())
.map(tick -> mApiService.SearchForOpponent(mGameInfo.GetGameID()))
.doOnError(err -> Log.e("Polling", "Error retrieving messages: " + err))
.retry()
.filter(new Func1<String, Boolean>()
{
#Override
public Boolean call(String _SearchOpponentResult)
{
Boolean OpponentExists;
if (_SearchOpponentResult != "0")
{
Log.e("Polling", "Filter " + _SearchOpponentResult);
OpponentExists = true;
}
else
{
OpponentExists = false;
}
return OpponentExists;
}
})
.take(1)
.subscribe(sub);
The emission is correct, however I get this log message on every emit:
E/Pollingļ¹• Error retrieving messages: java.lang.NullPointerException
Apperently doOnError is triggered on every emit. Normally I would get some Retrofit debug logs on every emit which means that mApiService.SearchForOpponent won't get called. What do I do wrong?

Categories

Resources