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);
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 am trying to make webservie call using retrofit and rxjava 2. i was exploring two different approach to use RxJava2. problem is i am getting response whene i use Observable but it is not working with Flowable. Logs are not getting printed when using Flowable i tried to debug it but its not going inside onNext or onComplete or onError. only onSubscribe gets executed.
1) using observable as return type
new WebRequestManager().getContactObservable(userRequest)
.subscribe(new Observer<ResponseData>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(ResponseData responseData) {
Log.e(TAG , "data "+responseData.getStatus());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.e(TAG , "data complete");
}
}
);
2) Using flowable as return type
new WebRequestManager().getContactFlowable(userRequest)
.subscribe(new Subscriber<ResponseData>() {
#Override
public void onSubscribe(Subscription s) {
Log.e(TAG , "contact subscription ");
}
#Override
public void onNext(ResponseData responses) {
Log.e(TAG , "contact onNext ");
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
Log.e(TAG , "contact onComplete ");
}
});
Rest contact retrofit api
public interface ContactApi {
#POST(WebRequest.GET_CONTACTS)
Flowable<ResponseData> getContactFlowable(#Body UserRequest userRequest);
#POST(WebRequest.GET_CONTACTS)
Observable<ResponseData> getContactObservable(#Body UserRequest userRequest);
}
call to webservice
public Flowable<ResponseData> getContactsData(UserRequest userRequest){
return webRequest.getWebClient().create(ContactApi.class).getContacts(userRequest);
}
public Observable<ResponseData> getContact(UserRequest userRequest){
return webRequest.getWebClient().create(ContactApi.class).getContact(userRequest);
}
getting retrofit instance
public static Retrofit getWebClient(){
//if(okHttpClient == null)
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(120,TimeUnit.SECONDS)
.readTimeout(120,TimeUnit.SECONDS)
.writeTimeout(120,TimeUnit.SECONDS)
.addInterceptor(new WebRequestInterceptor("\"application/json\""))
.build();
// if(client == null)
client = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(LoganSquareConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return client;
}
With Subscribers, you have to call request to get items:
new WebRequestManager().getContactFlowable(userRequest)
.subscribe(new Subscriber<ResponseData>() {
#Override
public void onSubscribe(Subscription s) {
Log.e(TAG , "contact subscription ");
s.request(Long.MAX_VALUE); // <---------------------------------
}
#Override
public void onNext(ResponseData responses) {
Log.e(TAG , "contact onNext ");
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
Log.e(TAG , "contact onComplete ");
}
});
See also DisposableSubscriber with its example.
I'm trying to observe two requests with the same Observer. My Observer:
private BaseObserver<LoginResponse> loginObserver = new BaseObserver<LoginResponse>(this) {
#Override
public void onSubscribe(Disposable d) {
super.onSubscribe(d);
showLoading(true);
Log.d(TAG, "onSubscribe: ");
}
#Override
public void onNext(LoginResponse response) {
super.onNext(response);
Log.d(TAG, "onNext: ");
}
#Override
public void onComplete() {
super.onComplete();
showLoading(false);
Log.d(TAG, "onComplete: ");
}
#Override
public void onError(Throwable e) {
super.onError(e);
showLoading(false);
}
};
My request is a login request build with Retrofit2:
private void sendRequest(String username, String password) {
IResourceAPI iResourceAPI = RetrofitClient.createIResourceClient(this);
iResourceAPI.login(new LoginRequest(username, password))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(loginObserver);
}
Now I want to launch 2 or more requests and check the responses one by one in onNext, and when the last request is checked execute onComplete with my Observer. Could anyone help me please?
Thanks in advance.
You are looking to the merge operator:
You can combine the output of multiple Observables so that they act
like a single Observable, by using the Merge operator.
Just modify your request to return an Observable:
private Observable<LoginResponse> request(String username, String password) {
IResourceAPI iResourceAPI = RetrofitClient.createIResourceClient(this);
iResourceAPI.login(new LoginRequest(username, password))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
Then merge:
Observable.merge<LoginResponse>(request(...), request(...))
.subscribeWith(loginObserver);
It sounds like you are looking for merge operator.
I'm really new in RxJava, but I'm trying to implement an API using Retrofit framework and RxJava.
On a server side there is an authorization service which handles user's session and in case of some delay in user's actions server breaks his session. After that user has to login again in order to perform new API call. Bad thing is - server always returns HTTP code 200 and for notification about expiration uses some custom JSON response with expiration code, so RxJava doesn't fire Exception during onNext operation because RxJava considers that request was passed successfully.
And the question is: How to implement correct flow to handle custom API exceptions like expiration and retry failed request after some other request (in my case relogin)?
Something like this:
app -> login()
server -> { code:0, ... }
app -> getUsers()
server -> { code:0, ... }
------- in 30 minutes -------
app -> getPicture()
server -> { code:99, ... } // session expired, user unauthorized
app -> login()
server -> { code:0, ... }
app -> getPicture()
server -> { code:0, ... }
I was thinking about something like this, but with no success:
Observable.create(new Observable.OnSubscribe<BackendResponse<String>>() {
#Override
public void call(Subscriber<? super Response<String>> subscriber) {
try {
Response<String> response;
subscriber.onNext(response = getInterface().getUsers());
if (response != null) {
response.checkData(); // throws ServerException in case of code != 0
}
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.io()).retryWhen(new RetryWithSessionRefresh(new SessionService())).subscribe();
and RetryWithSessionRefresh is:
public class RetryWithSessionRefresh implements
Func1<Observable<? extends Notification<?>>, Observable<?>> {
private final SessionService sessionSerivce;
public RetryWithSessionRefresh(SessionService sessionSerivce) {
this.sessionSerivce = sessionSerivce;
}
#Override
public Observable<?> call(Observable<? extends Notification<?>> attempts) {
return attempts
.flatMap(new Func1<Notification<?>, Observable<?>>() {
#Override
public Observable<?> call(final Notification notification) {
final Throwable throwable = notification.getThrowable();
if (throwable instanceof ServerException) {
final ServerException backendException = (ServerException) throwable;
if (backendException.getBackendErrorCode() == Response.AUTHORIZATION_FAILED) {
return sessionSerivce
.observeSessionToken()
.doOnNext(new Action1<TokenCallback>() {
#Override
public void call(TokenCallback token) {
if (token != null) {
DataHolder.getInstance().setAuthToken(token.getToken());
}
}
})
.doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
DataHolder.getInstance().setAuthToken("");
}
});
}
}
return Observable.error(notification.getThrowable());
}
});
}
Maybe you van flapMap your response, and return your observable in case of success with Observable.just(your item) or an error when the response isn't valid with Observable.error(your error)