I have a Observable call with retrofit that zipped three API calls
but I want to have the 3 calls together , but sometime one of the calls fails but I only have one main call which is mandatory for me and the rest of calls is optional , because when one of them fails it do on Error , and I don't want that, I was thinking if there is like JoinObservable.when(OperatorJoinPatterns.or(call1 , call2 ) .then
but the only thing is and
Observable.zip(getSearchObservable(FIRST_PAGE), App.getApi().allbookmarks(), SpotlightUtil.getSpotLightBanner(), App.getApi().getFollowingSuggestions(AppConfigUtil.getFollowingSuggestions().getLimit()),
(searchResult, myFavouritesResults, spotlightListResult, followingSuggestionsResult) -> combineCall(searchResult, myFavouritesResults, spotlightListResult, followingSuggestionsResult, false))
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(spotlightsAndSearchResultAndSuggestionsResult -> {
//my main call that i want if that fail the request should fail
if (!NetUtils.isServerOk(spotlightsAndSearchResultAndSuggestionsResult.getSearchResult().getStatus())) {
throw new ServerErrorException(spotlightsAndSearchResultAndSuggestionsResult.getSearchResult().getErrorMessage());
}
if (spotlightsAndSearchResultAndSuggestionsResult.getSearchResult().posts.size() < PAGE_SIZE) {
rvPosts.setFinished(true);
}
hideLoader();
mPostAdapter.mSuggestions = spotlightsAndSearchResultAndSuggestionsResult.getFollowingSuggestionsResult().getSuggestion();
checkToAddOrRemoveFeedbackSpotLight(spotlightsAndSearchResultAndSuggestionsResult.getSearchResult().posts, true);
})
.doOnError(throwable -> {
ErrorScreenUtils.checkError(throwable, this, true);
hideLoader();
})
.retryWhen(RxActivity.RETRY_CONDITION).compose(bindUntilEvent(FragmentEvent.DESTROY))
.subscribe();
doOnError does not stop error propagation, so it breaks your logic.
For optional source use one of onErrorResumeNext, onErrorReturnItem, onErrorReturn operators. You can replace error with dummy value that can be successfully zipped:
Observable.zip(
source1,
source2,
optionalSource3.onErrorReturnItem(stub)
)
...
Related
I have a webservice call that return an object in which there is a parameter that indicates whether the operation ended successfully or not, so I would like to filter it (kind of if/else statement) inside the RxJava chain by using RxJava operators. Is it possible?
Something like this but not using if/else:
repo.webserviceCall(username, password)
.flatMap(result -> {
if (result.isSuccessful())
repo.secondWebserviceCall(result.getInfo())
else
showToastMessage("Api call not successful"); //STOP FLOW HERE
})
.flatMap(result -> thirdWebserviceCall(res))
.subscribe(res -> {showSuccessMssg(res)}, throwable -> { showError(t)});
You can return an error() from your flatMap so that the execution then goes to the onError consumer in your subscribe call.
If each service call returns one item, you could rearrange the operators so that not successful won't run the flatMap for the second and third calls. The filter will turn the setup to empty for which you can use the onComplete handler to display the toast.
repo.webserviceCall(username, password)
.filter(result -> result.isSuccessful())
.flatMap(result ->
repo.secondWebserviceCall(result.getInfo())
.flatMap(result -> thirdWebserviceCall(res))
)
.subscribe(
res -> showSuccessMssg(res),
throwable -> showError(t),
() -> showToastMessage("Api call not successful")
);
I am trying to chain two network calls in my Android app. I am using Retrofit. Basically I want to do :
Make API Call to login
Wait for the response of login, save the token to SharedPrefs
Make another API call right after I've saved the token
Wait for the response, save the data
I think I have chained the stream in the right way, the only thing is I want to update the UI in between. For example once the call starts I want to display a progressDialog ( I do that in doOnSubscribe ), or dismiss the Dialog once the call has completed ( I do that in doOnComplete ). However I get the exception Only the original thread that created a view hierarchy can touch its views. I subscribe on the io thread and observe on the mainThread so that I can make the changes to the UI, however I must be missing something.
I tried adding .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
lower in the stream, but I still get the same error message.
getView().onLoginAction().subscribe(aVoid -> Observable.combineLatest(
getView().userNameObservable().map(CharSequence::toString),
getView().passwordObservable().map(CharSequence::toString),
Pair::new)
.first()
.subscribe(usernamePasswordPair -> {
User user = User.create(usernamePasswordPair.first, usernamePasswordPair.second, "");
RetrofitClientInstance.createService(AuthenticationNetworkApi.class).login(new Login(user.username(), user.password()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(loginResponse -> {
AuthorizationResponse responseBody = loginResponse.body();
if (responseBody != null && responseBody.getAccessToken() != null && !responseBody.getAccessToken().isEmpty()) {
if (localStorage.getAccessToken().isEmpty()) {
localStorage.saveAccessToken(responseBody.getAccessToken());
}
}
}
).
doOnSubscribe( action -> getView().showProgressDialog())
.doOnError(error -> {
getView().dismissProgressDialog();
getView().showErrorMessage("Login Unsuccessful");
}).doOnComplete(() -> getView().dismissProgressDialog()
)
.flatMap(response -> RetrofitClientInstance.createService(ActivitiesApi.class).getUserActivities())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(activities -> {
for (UserActivityApiModel useractivity : activities
) {
activityService.addActivity(Activity.create(Integer.parseInt(useractivity.getId()), useractivity.getActivityName(), useractivity.getDate(),
Integer.parseInt(useractivity.getValue()), Integer.parseInt(useractivity.getSubCategory().getId())));
}
}).doOnError(error -> getView().showErrorMessage(error.getMessage()))
.doOnComplete(() -> getView().redirectToHomeScreen())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
}));
The error occurs here :
.doOnError(error -> {
getView().dismissProgressDialog();
getView().showErrorMessage("Login Unsuccessful");
})
It seems you are using a different thread to execute your backend. In that case, you can't touch the main UI thread from the second one. You need to execute first runOnUiThread { //your code }
In //your code, call the two lines of code that you put on doOnError.
I'm using RxJava with a retrofit to make API calls,
By using RxJava methods like flatMap and map I'm making API calls as well as performing DB operations in room database on the background thread.
My implementation is perfect and working fine if there is no error, but In the case when I got an error or any exception while performing DB Operation, Application getting crashed due to following Rx error.
E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-1
The exception was not handled due to missing onError handler in the subscribe() method call.
I have used RxJava to perform my operation as below :
mDataManager.login(params)
.flatMap { loginResponse: LoginResponse ->
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap { object: SomeDataModel ->
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
// here making another API call based on previos API
return#flatMap mDataManager...
}.map {
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
return#map true
}
.observeOn(mSchedulerProvider.ui())
.subscribeOn(mSchedulerProvider.io())
.subscribe({ result ->
// updating view
}, { throwable ->
throwable.printStackTrace()
})
I'm getting an exception while inserting data to DB
Exception java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase:
But the error not handled and Application getting crash.
The error says that missing onError handler in the subscribe() method but in my code, I already override throwable to handle exception/errors.
Can anyone find, what I'm missing or what mistake I have done in code.
UPDATE
Found the solution, Mistake was here :
mDataManager.insertDataToDB(object).subscribe()
In the Above statement, I'm subscribing but was not handling the error for that and because of that error was not handled by rxJava and in the result, the application gets crashed.
Final Code as below :
mDataManager.login(params)
.flatMap { loginResponse: LoginResponse ->
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap { object: SomeDataModel ->
// avoid this inner subscribe
// mDataManager.insertDataToDB(object).subscribe()
return#flatMap mDataManager.insertDataToDB(object)
}
.flatMap {
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap {
// avoid this inner subscribe
//mDataManager.insertDataToDB(object).subscribe()
return#flatMap mDataManager.insertDataToDB(object)
}
.observeOn(mSchedulerProvider.ui())
.subscribeOn(mSchedulerProvider.io())
.subscribe({ result ->
// updating view
}, { throwable ->
throwable.printStackTrace()
})
Above code is working Fine!
The reason is you are subscribing here
.map {
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
return#map true
}
And this subscribe is not handling the error scenario.
I feel it's not a good practice to call subscribe() for the inner streams. In your scenario the flow is broken in-between.
The best way according to me is instead of using map and calling subscribe() here, use,
flatMap{
object -> mDataManager.insertDataToDB(object)
}
This way, if any error, it will be caught in last outer subscribe().
Hope this answer helps.
In an activity i need to call 4 rest API. One API does not depends on another. But for a calculation I need data of all 4 APIs. So, I'm planning to call APIs asynchronous but wait until all API are done loading. I'm using RxAndroid with retrofit. I don't know how to achieve this using rx. I don't want to use a Boolean for each API call to track if it loaded or not. Is there any rx way?
For now, I synchronize all calls like below
Sample code:
fun getData() {
val disposable = dataSource.getLivePayments()
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe(({ livePayments: List<Payment> ->
this.livePayments.clear()
this.livePayments.addAll(livePayments)
dataSource.getLiveRemittances()
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe(({ liveRemittances: List<Remittance> ->
this.liveRemittances.clear()
this.liveRemittances.addAll(liveRemittances)
// similarly called dataSource.getLiveTimeSheets()
// and dataSource.getArchiveTimeSheets()
// synchronous
// then call
calculateAmount()
}), ({ t -> this.parseError(t) }))
}), ({ t -> this.parseError(t) }))
compositeDisposable.add(disposable)
}
fun calculateAmount() {
val balance = if(liveRemittances.isNotEmpty()) {
(sum(payment.amount) - sum(timesheet.amount)) * sum(liveRemittance.amount)
} else {
(sum(payment.amount) - sum(timesheet.amount))
}
}
NB: In this procedure, if some API fails, it stop executing next API but i want it should call next API.
If all the API calls are independent you can use the zip operator:
Single.zip(single1, single2, ..., (result1, result2, ... -> (combine the results)})
If the have dependant results you can use flatMap
single1.flatMap(result -> (generate a new Single based on that result)})
I am new to RxJava. My experience is mainly from those tutorials:
Here, and here.
Now, I found myself in a situation where I have API call with Retrofit2 that will return an Obesrvable<AccessToken>. The client will call this service as follows:
public Observable<TokenResult> authenticateWithClientCredentials() {
return authService.authenticate("client_credentials").take(1);
}
Where a class called Authenticator will call #authenticateWithClientCredentials() from the client.
What I would like to achieve is to return an Observable<Boolean> from the Authenticator class, once the API call is finished, i.e. inside onComplete() to indicate that the access token has been fetched and saved in cache inside the Authenticator class successfully.
I tried the defer operator but I'm still lost.
EDIT
I know I can pass a callback in the parameters, but isn't the idea of Rx to replace the old classic callbacks approach?
If I have understood your question correctly, then this is what you are searching:
client.authenticateWithClientCredentials()
.map(token -> {
// save `token` into cache
return token;
})
.flatMap(integer -> Observable.just(Boolean.TRUE))
.subscribe(aBoolean -> {
// success
}, throwable -> {
// error
});
I do not know why exactly you want Observable<Boolean>, Completable is much more preferred in this case, because either the stream has successfully completed or no. Nevertheless, posted the solution using Observable<Boolean> return type.
Here's the Completable approach:
client.authenticateWithClientCredentials()
.flatMapCompletable(integer -> {
// save `token` into cache
return Completable.complete();
})
.subscribe(() -> {
// success
}, throwable -> {
// error
});