I'm still trying to figure out observers in RxJava. I have a Retrofit client, a utility methods and a class that manages a data syncing feature. When I attempt to observe my observables I am not seeing any indication that the observer is subscribed.
My Utility method
public static Single<Response<SyncResponse>> getSyncData() {
Single response = FestApiClient.getInstance().postSyncData()
.observeOn(Schedulers.newThread())
.subscribeOn(AndroidSchedulers.mainThread());
return response;
}
My retorfit client
public Single<Response<SyncResponse>> postSyncData() {
Single response = mFestApiClientService.postEventSyncList("my endpoint");
return response;
}
And my data syncing manager
Disposable syncDisposable = ScheduleUtils.getSyncData().subscribe(syncResponse -> {
if (syncResponse.isSuccessful()){
Log.d(TAG, "Successfully posted events!");
addEventsFromSync(syncResponse.body());
mSyncDialog.dismiss();
} else {
getFailureMessage(syncResponse.body());
Log.d(TAG, "Failed posting events");
}
}, throwable -> {
Log.d(TAG, "Failed posting events");
});
mCompositeDisposable.add(syncDisposable);
I thought that the syncResponse.onFailure and onSuccess methods would be hit, but i never see the log messages or hit break points. I'd appreciate if you let me know if you see anything smelly. thanks!
You are observing on the wrong thread. Switch AndroidSchedulers so that it is on the main thread.
public static Single<Response<SyncResponse>> getSyncData() {
Single response = FestApiClient.getInstance().postSyncData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
return response;
}
Related
I am trying to send an io.reactivex.Flowable from a Spring RestController to an Android application that uses Retrofit and Rxjava. If I use the browser to check what the Rest endpoint returns, I get a series of values as expected but in Android I get only one value and then it calls the onComplete method. What am I missing?
Spring Controller:
#GetMapping("/api/reactive")
public Flowable<String> reactive() {
return Flowable.interval(1, TimeUnit.SECONDS).map(sequence -> "\"Flowable-" + LocalTime.now().toString() + "\"");
}
Retrofit repository:
#GET("reactive")
Flowable<String> testReactive();
Main service:
public useReactive() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Values.BASE_URL)
.addConverterFactory(JacksonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
userRepository = retrofit.create(UserRepository.class);
Flowable<String> reactive = userRepository.testReactive();
Disposable disp = reactive.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new ResourceSubscriber<String>() {
#Override
public void onNext(String s) {
logger.log(Level.INFO, s);
Toast.makeText(authActivity, s, Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable t) {
t.printStackTrace();
}
#Override
public void onComplete() {
logger.log(Level.INFO, "Completed");
Toast.makeText(authActivity, "Completed", Toast.LENGTH_SHORT).show();
}
});
}
Upon calling the useReactive() method, I get only one value "Flowable-..." and then "Completed".
Even though the Retrofit service has return type Flowable<String>, calling testReactive() will only make one HTTP call on the Android device.
The type Flowable is merely for compatibility, in practice it will end up being a Flowable that emits a single value and then terminates.
This is just how Retrofit works.
You would need to find another solution if you want to continually receive new values that are being emitted from the server, perhaps GRPC or polling the server.
I'm using RxJava's flatmap in order to execute multiple calls in sequence where one call relys on the previous call. I also need to know which observable emitted an error in the case that onError is called in order to properly implement my error handling. How do I achieve this?
Here is my code:
mSubscription = RxUtil.callObservable(mDataManager.createAccount(email, password))
.flatMap(new Func1<AuthResult, Observable<Void>>() {
#Override
public Observable<Void> call(AuthResult authResult) {
User user = new User(0, null, null, name, null, username, 0, 0);
return RxUtil.callObservable(mDataManager.createUser(authResult.getUser().getUid(), user));
}
})
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
try {
throw (Exception) throwable;
} catch (FirebaseAuthUserCollisionException ucException) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_exists));
} catch (Exception exception) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_general));
}
}
#Override
public void onNext(Void aVoid) {
getPickUpView().createAccountSuccessful(authResult);
}
});
I was thinking about this the wrong way. Is summary, I thought this was an issue that I needed to address when i didn't. RxJava will emit all errors in the onError method no matter what observable emits the error. Once onError is called the subscription is done, so the flatmap call will never take place.
In summary, all I need to do is handle my errors from both observables I call (the original and the one in the flatmap) in the same onError method.
I have a request call which can return 200 if an user has been subscribed to an event, 204 if the user is not subscribed or 404 if the event no longer exist.
I'm using retrofit 2 and Observables for calling the server.
how can I check If I have a code 200 or 204?
If I got a 404 I know it is an error and I easily deal with it, but I the response is different I don't know how to get the actual code.
mApiEvents.isSubscribed(idEvent, uniqueId )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(getLifecycleProvider())
.flatMap( data-> {
return ?? //How do I check if I got 200 or 204?
}
.subscribe(new LifecycleObserver<Boolean>(){
#Override
public void onNext(Boolean aBoolean) {
}
#Override
public void onError(Throwable e) {
//404 or another error
}
}
)
I finally found it, at the end the solution is quite straight forward.
we have to use a Retrofit2 Response class for wrapping our data.
https://square.github.io/retrofit/2.x/retrofit/retrofit2/Response.html
#POST("some/endpoint")
Observable<Response<Data>> getData(...)
then we can manipulate it with a flatMap and check the Code
getData()
....
.flatMap( response -> {
if(response.code() == 200)
//do something
else
//do something else
}
Not really what you asked, but you can check isEmpty(), because as the body of a 204 is empty, retrofit won't emit any item and you'll receive an onComplete without any onNext.
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));
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