RxJava 2 Observable with flatMapCompletable doesn't complete - android

I have an observable that emits items and upload them to server.
Here is the code:
repository
.getItems()
.doOnComplete(() -> Log.d(TAG, "No items left."))
.flatMapCompletable(item ->
repository
.uploadItems(item)
.onErrorComplete()
.andThen(
deleteTemporaryItem()
.onErrorComplete()
)
);
getItems method emits items one by one and then completes, uploadItems method upload them to server. The issue is when there is no items all chain onComplete event just working fine and all my subscribers get this event and proceed it BUT when there were some items and all of them were uploaded onComplete events doesn't go further than .doOnComplete(() -> Log.d(TAG, "No items left.")) method and all subscribers doesn't get this event. I added onErrorComplete to be sure that all methods after uploadItems completes and I also see in logs that all of them were completed but onComplete event from repository.getItems() doesn't go to all subscribers.
Could anyone please help to figure out what could be the reason for this behavior?
Thanks in advance!

Please have a look at this example:
I pass the item through each step, so the subscribe will be notified on each item that has been processed. The processing pipeline involves uploading and deleting the file.
Please try to change the implementation and post a log of the output.
#Test
void name() throws Exception {
Flowable<Integer> completed_work = Flowable.just(1, 2, 3)
.map(integer -> integer * 1000)
.flatMapSingle(integer ->
Completable.fromAction(() -> {
Thread.sleep(integer);
// do upload stuff here
})
.doOnComplete(() -> System.out.println("Uploaded file ...."))
//.timeout(10, TimeUnit.SECONDS)
.retry(3)
.andThen(
Completable.fromAction(() -> {
// do delete stuff...
})
.retry(2)
//.timeout(10, TimeUnit.SECONDS)
.doOnComplete(() -> System.out.println("Deleted file ..."))
)
.toSingle(() -> integer)
)
.doOnComplete(() -> System.out.println("Completed work"));
completed_work.test()
.await()
.assertResult(1000, 2000, 3000);
}

Related

RxJava if/else with multiple chain calls

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

Passing List to RxJava Concat and when remove item in onNext or onError causes ConcurrentModificationException

I have this code which I can use with concat but what I need is a way of passing Iterator so that I can add or remove items which isn't possible with this code.
I want to be able to remove and add items to concat without causing java.util.ConcurrentModificationException
I need to do this for different purpose such as if some items are success remove them and retry others. Or if token is invalid then remove all and refresh token and add all with modified token.
Observable userObservable = getUserObservable(token);
Observable userObservable = getMerchantObservable(token);
List<Observable<?>> observables = Arrays.asList(userObservable, merchantObservable);
Observable.concat(observables)
.doOnNext(
result -> observables.remove(0)
//then if token refreshed call is success then
//observables.clear()
//observables.add(updatedUserObservable)
//observables.add(updateMerchantObservable)
)
//.doOnError(error -> System.out.println(error.getMessage()))
.doOnError(error -> {
System.out.println(error.getMessage());
if(((HttpException) error).code() == 401){
observables.clear();
observables.add(getRefreshTokenObservable());
}
})
.retryWhen(new RetryWithDelay(5, 2000))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
result -> System.out.println(result),
error -> System.out.println("onError called!"));

Only the original thread that created a view hierarchy can touch its views when chaining Observables

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.

RxAndroid Re-Subscribe to Observable onError and onComplete

My Question is probably more of the conceptual nature.
I get that by the Observable contract my Observable will not emit any more items after onComplete or onError is called.
But I'm using the RxBindings for Android and therefore it's not "my Observable" but the click on a Button that emits items.
fun observeForgotPasswordButton(): Disposable {
return view.observeForgotPasswordButton()
.flatMap {
authService.forgotPassword(email).toObservable<Any>()
}
.subscribe({
// on next
Timber.d("fun: onNext:")
}, { error ->
// on error
Timber.e(error, "fun: onError")
}, {
// onComplete
Timber.d("fun: onComplete")
})
}
observeForgotPasswordButton() returns an Observable
fun observeForgotPasswordButton(): Observable<Any> = RxView.clicks(b_forgot_password)
The problem is that authService.forgotPassword(email) is a Completable and it will call either onComplete or onError both of which lead to the fact that I cannot reuse the button anymore since the subscription ended.
Is there a way to circumvent this behavior?
Because in an error occurs I would like to be able to retry.
Also I would like it to be possible to send more then one password forgotten emails.
You can use the retry() and repeat() operators to automatically resubscribe to the original Observable (or Completable).
fun observeForgotPasswordButton(): Disposable {
return view.observeForgotPasswordButton()
.flatMap {
authService.forgotPassword(email).toObservable<Any>()
}
.repeat() // automatically resubscribe on completion
.retry() // automatically resubscribe on error
.subscribe({
// on next
Timber.d("fun: onNext:")
}, { error ->
// on error
Timber.e(error, "fun: onError")
}, {
// onComplete
Timber.d("fun: onComplete")
})
}

RxJava2 subscribe is not called when mapping observable to completable

I have a click event which needs to make a network request.
RxView.clicks(button)
.flatMapCompletable({ x -> networkCall() })
.subscribe(...)
The click is an Observable.
networkCall returns a Completable.
However the block inside subscribe is never called when i tap the button.
I've also tried
RxView.clicks(button)
.flatMap({ x -> networkCall().toObservable<Void>() })
.subscribe(...)
How can I get this to work so that each time I tap on the button, a network request is made and is then handled in the subscribe.
EDIT:
I haven't done the network stuff yet so currently it's just
public Completable networkCall() {
Completable.complete();
}
So it's guaranteed to complete.
The flatMap case needs items, otherwise its onComplete will never fire due to the already mentioned never-completing clicks source. For example:
RxView.clicks(button)
.flatMap({ x -> networkCall().andThen(Observable.just("irrelevant")) })
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ v -> System.out.println(v)}, { e -> e.printStackTrace() })

Categories

Resources