How to delay onError() in RxJava 2 and Android?
Asked Answered
H

3

5

I'm Trying to load some data from a web server in my application. And Because of the async nature of the operation, there is no way to know ahead of time how long it will take to complete. To alert the user that the operation is “in progress.”, I'am using a loading indicator.

This is the a came up with using kotlin and RxJava 2 ( I hope it's clear):

fun loadData(){

    showLoader() // show loading indicator

    Single.fromCallable {

        // http request logic goes here

    }.delay(1000, TimeUnit.MILLISECONDS)
     .subscribeOn(Schedulers.io())
     .observeOn(AndroidSchedulers.mainThread())
     .subscribeWith(object : DisposableSingleObserver<String>() {

            override fun onSuccess(data: String) {
                // do something
                hideLoader() // on success, hide indicator
            }

            override fun onError(e: Throwable) {
                displayErrorMessage()
                hideLoader() // on error hide indicator
            }

      })
}

I want to show the loading indicator for at least 1 second so I used the delay() operator, but the problem is this works as expected if the operation succeeded, but in case of an error, the indicator will disappear immediately not after 1 second.

So is there a way I can make the onError() method execute after 1 second? Thanks

Herzegovina answered 1/12, 2017 at 15:50 Comment(3)
Can't you just go with Observable.timer(1, TimeUnit.SECONDS).switchMapSingle { Single.fromCallable { //... } }?Sequestration
mcassino thank you this works perfectly. Is it not possible to apply the timer() operator directly on the Single instead of converting it to an Observable first?Herzegovina
You can :) I think Single.timer() then map() is also a valid way of doing it.Sequestration
H
3

Thanks to nacassiano comment, I finally manage to find a solution:

fun loadData(){

    showLoader() // show loading indicator

    Single.timer(1000, TimeUnit.MILLISECONDS)
        .flatMap{
            Single.fromCallable {

                // http request logic goes here

            }
        }.subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribeBy( // this is an extension function from rxkotlin
            onSuccess = {
                // do something
                hideLoader() // on success, hide indicator
            },
            onError = {
                displayErrorMessage()
                hideLoader() // on error hide indicator
            }
       )
}

I hope this will help someone.

Herzegovina answered 1/12, 2017 at 18:39 Comment(0)
H
2

Just call a different delay method signature

public final Single<T> delay(long time, TimeUnit unit, boolean delayError)

i.e.

Single.fromCallable {

    // http request logic goes here

}.delay(1000, TimeUnit.MILLISECONDS, true)
Hyperborean answered 19/7, 2021 at 13:53 Comment(0)
R
1

Since your work is done in a background Thread you can just sleep it without freezing the ui:

SystemClock.sleep(1000)

just put this before the hideLoader() call

Rightly answered 1/12, 2017 at 16:24 Comment(2)
Good idea, but isn't that what delay() is supposed to do?Herzegovina
In the documentation of delay it says Returns an Observable that delays the emissions of the source ObservableSource ..So it delays the emission, not the execution. In theory your code could fail before the delayed emission kicks in as I understand it.Rightly

© 2022 - 2024 — McMap. All rights reserved.