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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With