Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxAndroid - retry observable on click

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?

like image 979
Michael Avatar asked Apr 23 '16 10:04

Michael


1 Answers

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.

like image 82
Roman Iatcyna Avatar answered Nov 15 '22 00:11

Roman Iatcyna