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?

Michael
  • 187
  • 1
  • 2
  • 7
  • What is triggering a request? I cannot see that from your code. Does calling `cities()` trigger a new request? – ByteWelder Apr 23 '16 at 10:52
  • I've added more code above. Yes, `cities()` trigger a new request – Michael Apr 23 '16 at 11:07
  • Then you can just wrap the whole `citiesService.cities()...` reactive chain into a new method and call that from your Snackbar's action listener. – ByteWelder Apr 23 '16 at 11:08

1 Answers1

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.

Roman Iatcyna
  • 394
  • 3
  • 10
  • 1
    I have also tried this approach, taken from https://android.jlelse.eu/rx-grokking-retrywhen-and-repeatwhen-on-android-development-examples-af5c3ed0872b HOWEVER Android Studio shows the message `Cannot resolve method asObservable()` – Someone Somewhere Jan 11 '18 at 22:25
  • 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 click – Neha Jan 19 '18 at 04:27