Шаблоны для реактивных потоков, работающих с пользовательским вводом и сетями - PullRequest
0 голосов
/ 10 сентября 2018

Я использую RxKotlin для создания своего последнего приложения для Android, и я столкнулся со знакомой проблемой: как обрабатывать сетевые ошибки в стиле Rx.

У меня настроен поток для условий поиска против TextView, например:

searchBar
  .queryTextObservable()
  .debounce(500, TimeUnit.MILLISECONDS)
  .map { it.trim() }
  .filter { it.isNotBlank() }
  .observeOn(Schedulers.io())

Это полезный способ прослушивания изменений при вводе текста, поэтому я затем расширяю код для подачи подготовленного текста в сетевой запрос (используя библиотеку Retrofit с расширением RxJava) для поиска по:

searchBar
  .queryTextObservable()
  .debounce(500, TimeUnit.MILLISECONDS)
  .map { it.trim() }
  .filter { it.isNotBlank() }
  .observeOn(Schedulers.io())
  .switchMap { search(it) }
  .observeOn(AndroidSchedulers.mainThread())
  .subscribeOn(AndroidSchedulers.mainThread())
  .subscribe(...)

Проблема возникает при сетевой ошибке - вся моя подписка отменяется. Похоже, у меня есть несколько вариантов для управления сбоем, но ни один из них не выглядит очень чистым:

  • Иметь внутреннюю наблюдаемую после завершения ввода текста, которая делает сетевой запрос
  • Используйте onErrorResumeNext и передайте дозорное значение

Это, очевидно, не является исчерпывающим, но каковы подходящие шаблоны для изящной обработки сетевых ошибок при сохранении потока (и, следовательно, полезности) пользовательского ввода из панели поиска?

1 Ответ

0 голосов
/ 11 сентября 2018

Использование таких операторов, как onErrorReturn, является довольно стандартным подходом, если вы посмотрите на реактивные шаблоны, которые обеспечивают однонаправленный поток данных, такой как MVI.

Следуя таким шаблонам, вы обычно отображаете состояние вашего сетевого вызова в объекткоторый представляет состояние вызова.

Простой пример без каких-либо шаблонов MVx будет выглядеть примерно так, как показано ниже, где наблюдаемое из RXBinding вызывает вызов API, но вместо простого возврата данных из APIон возвращает объект состояния, который затем можно отобразить на экране.

private val disposables = CompositeDisposable()

override fun onStart() {
    super.onStart()
    disposables.add(
        RxView.clicks(load_data_button)
            .flatMap { requestData() }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(this::renderRequestState)
    )
}

override fun onStop() {
    disposables.clear()
    super.onStop()
}

private fun requestData(): Observable<RequestState> {
    return myApi.requestData()
        .toObservable()
        .subscribeOn(Schedulers.io())
        .map<RequestState>(RequestState::Success)
        .onErrorReturn(RequestState::Error)
        .startWith(RequestState.InFlight)
}

private fun renderRequestState(requestState: RequestState) {
    when (requestState) {
        RequestState.InFlight -> showProgress()
        is RequestState.Success -> showResult(requestState.result)
        is RequestState.Error -> showError(requestState.error)
    }
}

sealed class RequestState {
    object InFlight : RequestState()
    data class Success(val result: MyData) : RequestState()
    data class Error(val error: Throwable) : RequestState()
}

Ханнес Дорфманн написал большой набор статей по шаблону MVI, в котором используется этот подход.

...