Combine requests with Retrofit2 and RxJava2 - android

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.

Related

RxJava + Websocket - How to add Observable to Websocket listener?

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.

retrofit2 rxjava 2 - how can access to body of response when have error

I use Retrofit with Rxjava together for request to server.
the my server return defined json format that Include data , and defined message.
server return http response. it's ok if server return success code(200).
but I want, if server return other code, i manage the body of that response.
for example:
the server return 401, and I want read body of response for show message of the server.
but when server other code, retrofit call the onError method and I can't use body of response.
how can solve this problem?
this is my mehod
'''
private void login(String username , String password){
view.setLoading();
source.loginUser(username, password)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response<LoginResult>>() {
#Override
public void onSubscribe(Disposable d) {
disposable.add(d);
}
#Override
public void onSuccess(Response<LoginResult> loginResult) {
if (loginResult.isSuccessful()){
}
else
new AlertConfiguration(view.getViewActivity()).showMessage(loginResult.body().getMessage());
}
#Override
public void onError(Throwable e) {
if there is a problem
}
});
'''
and this is my interface method for retrofit
#POST("...")
Single<Response<LoginResult>> loginUser(#Query("username") String username, #Query("password") String password);
According to info here, since you are already using Response<LoginResult> you should be able to cast the Throwable to a HttpException, and then extract the body of the response.
In Kotlin:
if (e is HttpException) {
val errorBody = (e as HttpException).response().errorBody()
}
In Java:
if (e instanceof HttpException) {
HttpException error = (HttpException)e;
String errorBody = error.response().errorBody().string();
}
Complete example using your Java code:
import retrofit2.HttpException
private void login(String username , String password){
view.setLoading();
source.loginUser(username, password)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response<LoginResult>>() {
#Override
public void onSubscribe(Disposable d) {
disposable.add(d);
}
#Override
public void onSuccess(Response<LoginResult> loginResult) {
if (loginResult.isSuccessful()){
// Handle success
} else {
// Handle login failure
new AlertConfiguration(view.getViewActivity()).showMessage(loginResult.body().getMessage());
}
}
#Override
public void onError(Throwable e) {
// Get the error response:
if (e instanceof HttpException) {
HttpException error = (HttpException)e;
String errorBody = error.response().errorBody().string();
// Then parse the errorBody and extract the values you need
}
}
});
I find solution
in retrofit onSuccess methode when response code is 200, you should get response from body object.
but when isn't 200, you should get response from errorBody;
new Gson().fromJson(serverResultResponse.errorBody().string(), ServerResult.class);

SocketException unsubscribing request without onError

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);

RxJava 2, Retrofit 2 + Retrolambda - chaining 2 requests together

The first API call returns a list of elements and I then want to subsequently call another API with a String returned in each element of the list from the first API call. I (think I) have got it so that it's calling the second API call with each element of the list but I am unsure how to then subscribe to that to get the results returned from the second call.
discogsService.getSearchResults(searchTerm, mContext.getString(R.string.token))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
// Turns the result into individual elements
.flatMapIterable(RootSearchResponse::getSearchResults)
// I believe this then calls .getRelease() with each ID string
.map(result -> discogsService.getRelease(result.getId()));
Retrofit Interface:
public interface DiscogsService
{
#GET("database/search?")
Observable<RootSearchResponse> getSearchResults(#Query("q") String searchTerm, #Query("token") String token);
#GET("releases/")
Observable<Release> getRelease(#Query("release_id") String releaseId);
}
I'm unsure where to go from here.
I believe .subscribe(...) then gives me the ability to get the Observable<Release> returned from each .getRelease(...). As the above method is called in the Model layer I then need to set up a subscriber in this model layer to pass back to the Presenter and then an additional subscriber in the Presenter to deal with each Observable as the Presenter has access to the View.
Is there a way so that I can just return each Observable from the Model layer so I don't need to have two separate .subscribe(...)s? Or should I use two separate .subscribe(...)s as I can then catch errors on the both of them? I only want the results from the second call.
Here is the full code that I have tried:
In Model:
discogsService.getSearchResults(searchTerm, mContext.getString(R.string.token))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.flatMapIterable(RootSearchResponse::getSearchResults)
.subscribeOn(Schedulers.io())
.map(result -> discogsService.getRelease(result.getId()))
.subscribe(new Observer<Observable<Release>>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Observable<Release> value)
{
mainPresenter.addToRecyclerView(value);
}
#Override
public void onError(Throwable e)
{
}
#Override
public void onComplete()
{
}
});
In Presenter:
#Override
public void addToRecyclerView(Observable<Release> value)
{
value .observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Release>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Release value)
{
Log.e(TAG, "Success! " + value);
results.add(value);
}
#Override
public void onError(Throwable e)
{
Log.e(TAG, "Error: " + e.toString());
Log.e(TAG, "Error: " + e.toString());
}
#Override
public void onComplete()
{
}
});
I would rather expose an Observable<Release> at model level:
Observable<Release> getReleases(...) {
return discogsService.getSearchResults(...)
.flatMapIterable(RootSearchResponse::getSearchResults)
.flatMap(result -> discogsService.getRelease(result.getId()));
}
Presenter would just subscribe to it:
getReleases
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Release>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Release value)
{
Log.e(TAG, "Success! " + value);
results.add(value);
}
#Override
public void onError(Throwable e)
{
Log.e(TAG, "Error: " + e.toString());
Log.e(TAG, "Error: " + e.toString());
}
#Override
public void onComplete()
{
}
});
Only one Observable. Note the switch from map() to flatMap() for the second request in getReleases(...). Behind the scene this is where occur the second subscribe.
The final subscribe will receive errors from both requests. I prefer to let the consumer (Presenter) handle errors, because it's the one who care about the response and know what to do in case of errors (displaying a message for example).
It's the one who 'drive' the Observable, who create, dispose it, so it's also his duty to assign thread imho.
Observable make very good contract to expose from one layer to another. It describe the data type, how to consume it and the pattern (Observable ? Single ? Flowable ?).

retrofit 2 post rxjava (login)

I'm new using retrofit2 and rxjava, i was able to use GET to get information from api's but now, using POST for a login request is not working how is suposed too.
Application application = Application.get(mLoginView.getContext());
Service Service = application.getmService();
Log.i(TAG,""+username);
Log.i(TAG,""+password);
mSubscription = Service.login(username,password)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
Log.i(TAG,"User: " + mUser.getHash());
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
Log.i(TAG,"USERNAME DON'T EXIST");
}
#Override
public void onNext(User user) {
// LoginPresenter.this.mUser = user;
}
});
Service:
public interface Service {
String mUrl = "https://blabla.com/api/index.php/"; // TODO Change
#FormUrlEncoded
#POST("user/login")
Observable<User> login(#Field(value="email",encoded=true) String email, #Field(value="password",encoded = true) String password );
I enter a POST with username and pass from an existing user and return me a 404 page and not the hash im supposed to get.
Thank you
I forgot this was here, I found a solutions months ago, and what i did was create a object UserCredentials to do the body request and a object to get the response.
#POST("user/login")
Observable<LoginResponse> login(#Body UserCredentials userCredentials);

Categories

Resources