Resubscribe Observers on onError - RxJava - android

I have subscribed an observer as follows
public PublishSubject<ChannelModel> publisher = PublishSubject.create();
publisher.subscribe(observer);
Observer<ChannelListModel> observer = new Observer<ChannelListModel>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(ChannelListModel model) {
adapter.setChannels(model.getChannels());
}
};
It works perfectly fine the first time I request data and display the same. But if request fails and as soon as the onError is called, the publisher will lose its observer. I want to re-subscribe to the same observer. So that when I retry the failed request, I can publish the result to the observer.

In order to re-subscribe an Observable on error, you should use Retry operator. In RxJava, you can do it as follows
publisher
.retry()
.subscribe(observer);
After that subscription should be retried infinitely as long as error will be thrown.
You can also specify limit of possible retries like that:
publisher
.retry(5)
.subscribe(observer);
In the example above, subscriber will retry operation 5 times. When the error will be still persisted after 5 tries, then subscription will fail.

Related

Getting variables "into" RxJava chain for use later

I am using RxJava on Android to perform a login operation.
I need to pass in a username, password and a boolean flag. The username and password and sent to a server for verification and once a response is returned I need to use the flag to determine what to do next.
Since the login operation is asynchronous, I want to ensure that when the response returns that I will still have access to the username, password and flag that I passed in at the beginning.
Here is the initial way I coded this up that I believe has problems:
public Observable<Result> execute1(final String username, final String password, final boolean shouldSaveUsername) {
return mLoginNetwork
.loginWithCredentials(username, password)
.map(new Func1<Response<Void>, LoginObject>() {
#Override
public LoginObject call(Response<Void> response) {
if (!response.isSuccessful()) {
Exceptions.propagate(new HttpException(response));
}
return new LoginObject(username, password, shouldSaveUsername);
}
})
.doOnNext(new Action1<LoginObject>() {
#Override
public void call(LoginObject loginObject) {
if (loginObject.shouldSaveUsername) {
saveUsername(username);
}
}
})
.flatMap(new Func1<Entitlement, Observable<Result>>() {
#Override
public Observable<Result> call(LoginObject loginObject) {
return mNetwork
.fetchSomething();
}
});
}
When I call execute1() it returns an Observable which I cache and then subscribe to. If an Android configuration change occurs I unsubscribe from the Observable but keep it in a cache. Once the configuration change is complete I take the Observable out of the cache and resubscribe to it. When I resubscribe the loginWithCredentials call would need to be made again, but when it returns the username, password and boolean flag would no longer exist and therefore I wouldn't be able to use them in my chain which is a problem.
So, how to solve this issue?
I need a way for the input data to the Observable to become part of the Observable so that when I cache the Observable the input data is also cached.
Here is a proposed solution below:
public Observable<Result> execute2(String username, String password, boolean shouldSaveUsername) {
return Observable
.just(new LoginData(username, password, shouldSaveUsername))
.flatMap(new Func1<LoginData, Observable<LoginData>>() {
#Override
public Observable<?> call(final LoginData loginData) {
return mLoginNetwork
.loginWithCredentials(loginData.getUsername(), loginData.getPassword())
.map(new Func1<Response<Void>, LoginData>() {
#Override
public LoginData call(Response<Void> response) {
if (!response.isSuccessful()) {
Exceptions.propagate(new HttpException(response));
}
return loginData;
}
});
}
})
.doOnNext(new Action1<LoginData>() {
#Override
public void call(LoginData loginData) {
if (loginData.shouldSaveUsername) {
saveUsername(username);
}
}
})
.flatMap(new Func1<LoginData, Observable<Result>>() {
#Override
public Observable<Result> call(LoginData loginData) {
return mNetwork
.fetchSomething();
}
});
}
What I'm attempting to do is to make the input data part of the stream right away by using Observable.just() to take the input data and make it into an Observable and then let the rest of the downstream operations receive it as an input. I assume that if I now cache the observable and resubscribe later that the input data is now embedded in my observable and can be accessed in any of the operators later.
Have I solved my problem in my proposed solution in a "normal" RxJava / functional way? Are there better ways to approach this problem?
The username/password/save-flag are passed in to execute1() as parameters, marked as final. Then, in your anonymous nested classes, you make explicit references to those values, "closing over" them. The resulting observable chain has everything bound to it that it needs in order to operate.
Subscribing to the observable again will use the original username/password/save-flag.

Merge and handle two RxJava Observable of different types

My goal
I want to check if the server's token is still valid, let's say I know that information just by calling this getter : preferenceHelper.isTokenValid(). Then, if the token is invalid, calling a request to get a new token and updating the token locally, THEN, proceed with the next request to post the point to the server. That's because I need a valid token in order to make any further server request.
Let say I have those two server request that returns Observable:
This request is meant to get the server token, then upon reception, updating it.
Observable<Response<EntityToken>> updateServerToken = retrofitApi.authenticate(username,password);
This request is meant to post the current location to the server, then if it succeed, return the saved point
Observable<Response<EntityPoint>> updateServerToken = retrofitApi.postPoint(point);
Issues i'm facing currently:
Both observable that needs to be merged are from different type
Executing the token update request only if it needs to
Waiting for the token update request to complete before executing the request to post points
How should I write my RxJava Observable to satisfy all those condition?
First, I would create a method that checks if the entityToken is valid or not. If valid, use Observable.just() but you have to create an instance of Response somehow. If invalid, then call the server using the API in your requirement retrofitApi.authenticate(). Either path is taken, the method getTokenObservable() emits Observable<Response<EntityToken>>.
public Observable<Response<EntityToken>> getTokenObservable(EntityToken entityToken, String username, String password) {
boolean isTokenValid = preferenceHelper.isTokenValid(entityToken);
if (isTokenValid) {
//my assumption that you have something like this
Response<EntityToken> responseToken = new Response<EntityToken>();
responseToken.setEntityToken(entityToken);
return Observable.just(new Response<EntityToken>(entityToken.class));
} else {
Observable<Response<EntityToken>> updateServerToken = retrofitApi.authenticate(username, password);
return updateServerToken;
}
}
and then when calling it, use flatMap() which take emisssions of Observable<Response<EntityToken>> and returns emissions of Observable<Response<EntityPoint>>. Subscribe and proceed as normal.
Observable<Response<EntityToken>> updatePointObservable = getTokenObservable(entityToken, username, password);
updatePointObservable
.flatMap(new Func1<Response<EntityToken>, Observable<Response<EntityPoint>>>() {
#Override
public Observable<Response<EntityPoint>> call(Response<EntityToken> responseToken) {
EntityToken entityToken = responseToken.getEntityToken(); //my assumption
saveTokenLocally(entityToken); //this is where you save your token locally, change to the right method that you have
Observable<Response<EntityPoint>> updateServerTokenObservable = retrofitApi.postPoint(point, entityToken); //pass your entityToken to the call?
return updateServerTokenObservable;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<EntityPoint>>() {
#Override
public void onCompleted() {
//your own logic
}
#Override
public void onError(Throwable e) {
//your own logic
}
#Override
public void onNext(Response<EntityPoint> entityPoint) {
//your own logic
}
});
As there is a dependency between the three calls, merge does not make any sense. instead, use flatMap:
Observable<Response<EntityPoint>> response =
retrofitApi.isTokenValid()
.flatMap(isValid ->
isValid
? Observable.just("")
: retrofitApi.authenticate(username,password)
.doOnNext(token -> doSomethingWithTheToken(token)
)
.flatMap(dummy -> retrofitApi.postPoint(point));

Retrofit observable works only once

I understand that, by default, observables created by retrofit are "cold" observables.
I have this specific call to my server endpoint
#POST("oauth/token")
Observable<Token> signIn(#Field("username") String username, #Field("password") String password);
When I do:
public class LoginUseCase extends Subscriber<Profile> {
public void logIn(String username, String password) {
Subscription subscription = myApi.signIn(username, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this);
this.add(subscription);
}
}
I receive the onNext onError onComplete callbacks, as you would expect.
The problem arises when the login fails and I try again. Calling the login() method a second time doesn't trigger the http call, and I don't get any callbacks.
By the way, Im doing this on my onComplete() method
#Override
public void onCompleted() {
this.unsubscribe();
}
Is there a way to tell retrofit/rxandroid to re make the http call everytime I call myApi.signin(). Am I even approaching this the right way?
Notes:
- Im using dagger2 in my project and the myApi object is a singleton.
- I'm able to reproduce the error even when I use different username/pass configs between the first and second try
Once Subscriber#unsubscribe() is called that subscriber can never receive new values. You will need to recreate your subscriber each time you want to subscribe to a new observable.
What is happening is that in the call to Subscriber#add(Subscription) it sees that the subscriber has already been unsubscribed and immediately cancels the new subscription.

RxJava re-create observable onError

So I have my form model which holds all data I want to validate and then send to the server. Let's keep it as simple as possible - isFormValid or api request should return Observable.errr(throwable) which should call onError() in the subscriber.
public void submitForm(Form form){
Observable
.just(form)
.flatMap(form->{
if(isFormValid(form))
return Observable.just(form);
else
return Observable.error(someValidationError);
})
.flatMap(form->{
Request req = new Request(form);
try{
return Observable.just(getResponseFrom(req));
}
catch(ApiException e){
return Observable.error(e)
}
}).subscribe(
new Subscriber<ResponseModel>(){
#Override
public void onComplete(){}
#Override
public void onError(Throwable t){}
#Override
public void onNext(ResponseModel model){}
}
);
}
Ok, now let's say user enters invalid data, submitForm() is called and -sure enought- onError is called in subscriber and then onComplete. The user then enters valid data and submitForm() is called again.
Now here's the problem - in the second submitForm() call nothing happens! At least flatMap Func1 and the second flatMap Func2 are not called.
Why? What am I doing wrong ? Is it an architectural flaw?

Managing Auth Token using RxJava

I've been wondering what's the best way to tackle the issue of token refresh.
I'm connecting to an API which supplies me with a auth-token, if sometime time during the calls i get a INVALID_AUTH i need to re-authenticate.
So for the naive implementation i did this
#SuppressWarnings("unchecked")
#Override
public Observable<User> getUsers() {
return runCommandAndrefreshAuthIfNecessary(new RequestCommand() {
#Override
public Observable create() {
return createService(UsersApi.class).getUsers();
}
});
}
private Observable runCommandAndrefreshAuthIfNecessary(final RequestCommand command) {
return command.create()
.onErrorResumeNext(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(Throwable throwable) {
return handleRefreshToken(command);
}
});
}
private Observable<?> handleRefreshToken(final RequestCommand command) {
return refreshToken().flatMap(new Func1<Boolean, Observable<?>>() {
#Override
public Observable<?> call(Boolean aBoolean) {
return command.create();
}
});
}
As you can see i'm just wrapping the retrofit command, if i get an error i run refreshToken(), the token refreshes and i run the retrofit command again, so finally the Observable is passed back to the subscriber. Works as expected.
The thing i'm struggling with, is what happens i a multiple calls are made, for example, i'm calling getUsers and getFlags one after another. both of them get the INVALID_AUTH, currently both of the fire refreshToken(), which is bad.
i'm looking for a rx-java way to manage the calls, meaning after the first call of getUsers fires refreshToken, any call after that needs to wait for the refreshToken to end, only then fire the retrofit command.
Any suggestion will be appreciated.
You can use .cache() on the Observable for the token refreshing:
http://reactivex.io/documentation/operators/replay.html

Categories

Resources