Is there anyway to retry a retrofit http request when network connection available with Rx-java?
This is my request method
public DisposableObserver<List<Photo>> getNewPhotos(final int page,
int perPage,
String orderBy,
ObservableTransformer<List<Photo>, List<Photo>> observableTransformer,
final Callback<List<Photo>> callBack) {
return photoService.getPhotos(page, perPage, orderBy)
.compose(observableTransformer)
.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
return null;
}
})
.onErrorResumeNext(new Function<Throwable, ObservableSource<? extends List<Photo>>>() {
#Override
public ObservableSource<? extends List<Photo>> apply(Throwable throwable) throws Exception {
return Observable.error(throwable);
}
})
.subscribeWith(new DisposableObserver<List<Photo>>() {
#Override
public void onNext(List<Photo> value) {
callBack.onSuccess(value);
}
#Override
public void onError(Throwable e) {
callBack.onError(new NetworkError(e));
}
#Override
public void onComplete() {
}
});
}
i think maybe i could do something in retryWhen() method.
i want retrofit to trigger for internet connection and retry the last request when the connection is back.
i know the traditional way to retry but i think there must be a method or something in Rx-java to handle this.
if someone knows its good to share it with me.
Related
I have a ViewModel that is observing a RxJava Observable in my MainRepo class. I am trying to get my WebsocketListener in the MainRepo class to emit events, but I'm unsure how to do so.
MainRepo class:
private WebSocket ws;
public void createWsConnection() {
Request request = new Request.Builder()
.url(Constants.WEBSOCKET_ENDPOINT)
.addHeader(Constants.WEBSOCKET_HEADERS_KEY, Constants.USER_ID)
.build();
OkHttpClient client = new OkHttpClient
.Builder()
.pingInterval(30, TimeUnit.SECONDS)
.build();
this.ws = client.newWebSocket(request, webSocketListener);
}
This is where I'm confused. I don't know how I would use the websocket with the RxJava observable.
public Observable<String> createListener(){
return Observable.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(#NonNull ObservableEmitter<String> emitter) {
//I don't know what to put here in order to emit messages
//back to my ViewModel class using the websocket listener
}
});
}
The websocket listener:
private WebSocketListener webSocketListener = new WebSocketListener() {
#Override
public void onOpen(#NotNull WebSocket webSocket, Response response) {
Timber.d("Ws connection opened...", response.toString());
}
#Override
public void onClosing(#NotNull WebSocket webSocket, int code, #NotNull String reason) {
Timber.d("Ws connection closing...");
}
#Override
public void onClosed(#NotNull WebSocket webSocket, int code, #NotNull String reason) {
Timber.d("Ws connection closed...");
}
#Override
public void onMessage(#NotNull WebSocket webSocket, #NotNull String text) {
Timber.d("Ws incoming message.");
}
#Override
public void onFailure(#NotNull WebSocket webSocket, #NotNull Throwable t, Response response) {
Timber.e(t, "Ws connection failure.", response.toString());
}
};
A function in the ViewModel class that is observing the Observable in my MainRepo class:
public void connectToWs(){
mainRepo.createListener()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
Timber.d("Subscribed");
}
#Override
public void onNext(#NonNull String s) {
Timber.d("Message: " + s);
}
#Override
public void onError(#NonNull Throwable e) {
Timber.e(e, "Something went wrong.");
}
#Override
public void onComplete() {
Timber.d("On complete.");
}
});
}
Create a PublishSubject and change your createListener method to return it:
private PublishSubject<String> publishSubject = PublishSubject.create<String>();
public Observable<String> createListener(){
return publishSubject;
}
PublishSubject is an Observable so notice that you don't need to change your method signature, but I'd suggest you to rename the method name to something like observeMessages.
Then in your websocket listener you can emit the messages to the PublishSubject with onNext method. You should also call onComplete in the onClosed method and onError in the onFailure method:
private WebSocketListener webSocketListener = new WebSocketListener() {
#Override
public void onOpen(#NotNull WebSocket webSocket, Response response) {
Timber.d("Ws connection opened...", response.toString());
}
#Override
public void onClosing(#NotNull WebSocket webSocket, int code, #NotNull String reason) {
Timber.d("Ws connection closing...");
}
#Override
public void onClosed(#NotNull WebSocket webSocket, int code, #NotNull String reason) {
Timber.d("Ws connection closed...");
publishSubject.onComplete();
}
#Override
public void onMessage(#NotNull WebSocket webSocket, #NotNull String text) {
Timber.d("Ws incoming message.");
publishSubject.onNext(text);
}
#Override
public void onFailure(#NotNull WebSocket webSocket, #NotNull Throwable t, Response response) {
Timber.e(t, "Ws connection failure.", response.toString());
publishSubject.onError(t);
}
};
PublishSubject is the solution like Gustavo posted. But createListener() looks strange and i just want to show how i do it.
I'm not using OkHttp, but i do exactly same things with nv-websocket-client. It's just another websocket client.
In my scenario i do a lot of reactive stuff before, but the socket flow is:
connect to socket
register OnTextMessage listener - it should provides message to me
send messages to get permament answers from socket
Somewhere in OnCreate / OnCreateView:
// Creating socket with credentials
WebSocketFactory factory = new WebSocketFactory();
try {
socket = factory.createSocket("wss://ws.example.com", 3000);
} catch (IOException e) {
e.printStackTrace();
}
// Create a subject
PublishSubject<String> subject = PublishSubject.create();
I have done some HTTP/GET work before and save results in List<String> symbols - it also reactive way. After this i call subscribeToSymbols method which does a whole socket stuff:
public static Single<WebSocket> subscribeToSymbols(WebSocket socket,
PublishSubject<String> subject,
List<StockHttpGetData> symbols) {
// connect -> register onTextMessage callback -> sendMessages to Socket
return Single.fromCallable(socket::connect)
.subscribeOn(Schedulers.io())
.map(s -> s.addListener(new WebSocketAdapter() {
#Override
public void onTextMessage(WebSocket websocket, String text) {
subject.onNext(text);
}
}))
.doAfterSuccess(s -> symbols.forEach(httpData -> sendMessageToSubscribe(s, httpData)));
}
You are not interested in what subscribeToSymbols returns. The key point that subject.onNext(text) provides received message from socket to you if you subscribed to this subject.
Finally, subscribe to subject what you've created before and do what you want with message:
subject.flatMap(t -> Observable.just(new GsonBuilder()
.registerTypeAdapter(TreeSet.class, new SocketMessageDeserializer())
.create()
.fromJson(t, TreeSet.class)))
.filter(treeSet -> !treeSet.isEmpty())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(StocksRepository.getInstance()::printSocketMessage)
.subscribe(adapter::setStocksChanged, Throwable::printStackTrace);
The fact that it's complicated question because you should also handle connection errors, data pressure, config changes and be lifecycle-aware, but it also relevant for non-reactive way (e.g callback-hell), so PublishSubject is a start point.
Try to use PublishSubject, seems like it was created for cases like yours.
I am a new with Rxjava. I want to capture access token (received as part of headers) from the response of the first api call and then use that access token (as header) for subsequent api calls.
Note: the in the first api call, the response has no body,only headers are returned.
My Network Interface:
public interface NetworkInterface {
#POST("https://appauth.treuspan/Login")
Observable<Response<Void>> getAuthDetails(
#Header("Content-Type") String contentType,
#Body AuthRequestDetails authRequestDetails
);
#POST("https://appauth.treuspan//storedetails")
Observable<StoreDetailsResponse> getStoreDetails(
#Header("Content-Type") String contentType,
#Header("Accept") String accept,
#Header("Authorization") String authorization,
#Body AuthRequestBody authRequestBody
);
}
My observable methods:
public Observable<Response<Void>> getAuthObservable() {
return NetworkClient.getRetrofit().create(NetworkInterface.class)
.getAuthDetails("application/x-www-form-urlencoded",
new AuthRequestDetails().getAuthRequestDetails())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<Void>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Response<Void> voidResponse) {
String access_token_received = voidResponse.headers().get("access_token");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
The above doesn't compile. Its compile says:
Incompatible type:
required: io.reactivex.Observable<retrofit2.Response<java.lang.Void>>
Found: void
If I am able to successfully make the above call then i have to use the access token as part of header in the subsequent call as follows
public Observable<StoreDetailsResponse> getStoreDetailsObservable() {
return NetworkClient.getRetrofit().create(NetworkInterface.class)
.getStoreDetails("application/x-www-form-urlencoded",
"application/json",
"Bearer access_token_received",
new AuthRequestBody().getAuthRequestBody())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<StoreDetailsResponse>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(StoreDetailsResponse response) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
The above code also doesn't compile. It says:
Incompatible type:
required: io.reactivex.Observable<com.example.androidtest.model.StoreDetailsResponse>
Found: void
Please suggest how can i achieve this.
subscribe(...) will return a Subscription
Try to edit your code, make method return a Subscription like this
public Subscription getAuthObservable() {
return NetworkClient.getRetrofit().create(NetworkInterface.class)
.getAuthDetails("application/x-www-form-urlencoded",
new AuthRequestDetails().getAuthRequestDetails())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response<Void>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Response<Void> voidResponse) {
String access_token_received = voidResponse.headers().get("access_token");
}
});
}
I'm using Retofit2 and RxJava2 to fetch data from API. I am using the following code
Observable<Recipe> recipeObservable = getDataManager().getRecipes(String.valueOf(page));
recipeObservable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Recipe>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull Recipe recipe) {
getMvpView().updateRecipeList(recipe.getResults());
++page;
//getMvpView().hideLoading();
}
#Override
public void onError(#NonNull Throwable e) {
if (e instanceof HttpException)
Timber.d("Network Error");
}
#Override
public void onComplete() {
}
});
But the problem is Timber.d("Network Error"); never gets executed. I looked on various websites but could not solve the problem. I'm use to Retrofit2 but not fimiliar with RxJava2. So Please help. If more information is required please ask, I'll post it.
HttpException is not related to network failures. IOException is what you are looking for.
I'm trying to make a request using RxJava and Retrofit(2.3). I'm expecting that in case of any error I can retry or show a message to the client.
However, I notice that sometimes I have a SocketException which results in not calling onError, apparently the subscriber of the request just unsubscribes without calling anything else (not onComplete neither onError). Anyone knows why this is happening and how can I solve this in a generic way (without simply doing onUnsubscribe() and checking if the observable did not send any onError or onComplete)?
On my interface I have something like this:
#GET("userInfo")
Observable<List<UserInfo>> getUserInfo(#Header("token") String token);
This is how I create my observable:
public Observable<UserModel> requestUserInfo(final String token) {
return mService.getUserInfo(token)
.retryWhen(new RetryWithDelay(HTTP_RETRIES), HTTP_TIME_BETWEEN_RETRIES)))
.flatMap(new Func1<List<UserInfo>, Observable<UserModel>() {
#Override
public Observable<UserModel> call(List<UserInfo> userInfo) {
return Observable.just(new UserModel(userInfo));
}
});
}
------ UPDATE -------
This is how I call the requestUserInfo method on my presenter
private CompositeSubscription mCompositeSubscription = null;
public PresenterX(ViewX view) {
...
mCompositeSubscription = new CompositeSubscription();
}
public void getUserModel() {
String userToken = new AccessModel().getUserToken();
mCompositeSubscription.add(mNetworkRequestModel.requestUserInfo(userToken)
.flatMap(new Func1<UserModel, Observable<UserModel>>() {
#Override
public Observable<UserModel> call(UserModel userModel) {
if (userModel != null) {
saveUserModel(userModel); //sync saving
return Observable.just(userModel);
} else {
return Observable.error(new SaveException());
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<UserModel>() {
#Override
public void onCompleted() {
Log.i(TAG, "Subscriber was completed")
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "Subscriber called onError")
mView.handleErrors(e);
}
#Override
public void onNext(UserModel userModel) {
Log.i(TAG, "Subscriber called onNext")
mView.populateUserInfo(userModel);
}
}));
}
//called by activity when onDestroyMethod is called
//I assume this is not called as I have other requests running parallelly to this getUserModel() and they are not terminated, despite having other compositeSubscription to manage those
public void onDestroy(){
mCompositeSubscription.clear();
}
As I have a HttpLoggingInterceptor, this is the only log printed to me while the request suddenly stops.
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
#Override
public void log(String message) {
LOG.info(Thread.currentThread(), String.format("%s", message));
}
});
Here is the log exception:
I/ExampleApp-ApiClient(21338): : Thread: 1343 | <-- HTTP FAILED: java.net.SocketException: Socket closed
Because you try to get rx object (Observable) from server. You have incorrect function
Remove Observable
#GET("userInfo")
List<UserInfo> getUserInfo(#Header("token") String token);
This question already has answers here:
retrofit with rxjava handling network exceptions globally
(2 answers)
Closed 6 years ago.
I'm new in RxJava, but I like it. And now I have little problem.
I'm using RxJava + Retrofit.
If I have valid token for user, I get response for API, but if token is invalid, I must refresh token and try make request again.
Valid token:
Make reuqest
Get response
Done
Invalid token:
Make request
Get response
If response_code == 403, need refresh token
Refresh token
Here I want go to 1 step and make request to API again
Else - done
How to make it using RxJava?
Try this one has my knowledge
service.normalRequest()
.flatMap( new Func1<Response, Observable<Response>>() {
#Override
public Observable<Response> call(Response response) {
if (response.code() == 403) {
return service.refreshToken(refreshRequest)
.flatMap(new Func1<Response, Observable<Response>>() {
#Override
public Observable<Response> call(Response response) {
return service.normalRequest();
}
});
} else {
return Observable.just(response);
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<Response>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Response response) {
}
});
There is a special operator that can be used in this case:
public final Observable<T> retryWhen(final Func1<? super Observable<? extends Throwable>, ? extends Observable<?>> notificationHandler)
If you implement it you will see what parameters it accepts:
observable.retryWhen(new Func1<Observable<? extends Throwable>, Observable<T>>() {
#Override
public Observable<T> call(final Observable<? extends Throwable> error) {
}
});
You can see, that you get the error that was thrown by OkHttp. It is always throwing HttpException, if you call:
httpException.code()
You will get HTTP code number.
So the implementation of the function above might look like:
observable.retryWhen(new Func1<Observable<? extends Throwable>, Observable<T>>() {
#Override
public Observable<T> call(final Observable<? extends Throwable> error) {
return error.flatMap(doRelogging());
}
});
I have made a small library, that does what you expect:
RetrofitRxErrorHandler
You might try it or just look into sources for more advanced retry strategies regarding fetching API errors.