RxAndroid - retry observable on click
Asked Answered
A

1

6

I'm using rxAndroid and rxKotlin in my Android app to handle network requests asynchronously. Now I would like to retry a failed network request only after click on Snackbar button.

My code now:

val citiesService = ApiFactory.citiesService

citiesService.cities()
    .subscribeOn(Schedulers.newThread()) // fetch List<String>
    .flatMap { Observable.from(it) }     // convert to sequence of String
    .flatMap { city ->
        citiesService.coordinates(city)  // fetch DoubleArray
            .map { City(city, it) }      // convert to City(String, DoubleArray)
        }
    .toList()
    .observeOn(AndroidSchedulers.mainThread())
    .doOnNext {
        listView.setOnItemClickListener { adapterView, view, position, id ->
            onItemClick(it[position])
        }
    }
    .map { it.map { it.getName(activity) } }
    .subscribe(
        { listAdapter = setupAdapter(it) },
        { showErrorSnackbar() }  // handle error
    )

fun showErrorSnackbar() {
        Snackbar.make(listView, getString(R.string.not_available_msg), Snackbar.LENGTH_INDEFINITE)
                .setAction(getString(R.string.snack_retry_btn), {
                    // retry observable
                })
                .show()
    }

Cities interface for retrofit:

interface CitiesService {

    @GET("api/v1/cities")
    fun cities(): Observable<List<String>>

    @GET("api/v1/cities/{city}/coordinates")
    fun coordinates(@Path("city") city: String): Observable<DoubleArray>
}

Api factory:

object ApiFactory {

    val citiesService: CitiesService
        get() = retrofit.create(CitiesService::class.java)

    private val retrofit: Retrofit
        get() = Retrofit
            .Builder()
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(BASE_URL)
            .build()
}

How can I restart the observable in such way?

Action answered 23/4, 2016 at 10:16 Comment(3)
What is triggering a request? I cannot see that from your code. Does calling cities() trigger a new request?Bujumbura
I've added more code above. Yes, cities() trigger a new requestAction
Then you can just wrap the whole citiesService.cities()... reactive chain into a new method and call that from your Snackbar's action listener.Bujumbura
S
1

I can suggest you truly reactive way instead of imperative way.

Insert this code right after subscribe() method:

.retryWhen(retryHandler -> 
           retryHandler.flatMap(nothing -> retrySubject.asObservable()))
.subscribe()

Where update subject is just:

@NonNull
private final PublishSubject<Void> retrySubject = PublishSubject.create();

And on snackbar click call this method:

public void update() {
    retrySubject.onNext(null);
}

Everything above the retryWhen method will be literally redone.

Though with this approach error will never go down to the subscriber, you can add error conditioning to the retryHandler flat map, but this is another story.

P.S. sorry, this was Java code with retrolambdas, but you'll easily convert this to Kotlin.

Skunk answered 28/3, 2017 at 21:47 Comment(2)
I have also tried this approach, taken from android.jlelse.eu/… HOWEVER Android Studio shows the message Cannot resolve method asObservable()Superordinate
Got the same error as asObservable() method has been removed from RxJava2 but is there any other way I can get the same functioning for retry on clickSlam

© 2022 - 2024 — McMap. All rights reserved.