首页 > 解决方案 > 处理用户输入和网络的反应式流模式

问题描述

我正在使用 RxKotlin 构建我最新的 Android 应用程序,并且遇到了一个熟悉的问题:如何以类似 Rx 的方式处理网络错误。

我针对这样的搜索词设置了一个流TextView

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

这是侦听文本输入更改的有用方法,因此我扩展代码以将准备好的文本输入网络请求(使用带有 RxJava 扩展的 Retrofit 库)以搜索:

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(...)

出现网络错误时会出现问题 - 我的整个订阅都被取消了。似乎我有一些选项来管理故障,但它们看起来都不是很干净:

这显然不是详尽无遗的,但是在保留来自搜索栏的用户输入流(以及因此有用性)的同时优雅地处理网络错误的适当模式是什么?

标签: androidkotlinretrofit2rx-java2

解决方案


如果您查看提供单向数据流(例如 MVI)的反应模式,使用诸如 onErrorReturn 之类的运算符是一种非常标准的方法。

按照这样的模式,您通常会将网络调用的状态映射到表示调用状态的对象中。

一个没有任何 MVx 模式的简单示例如下所示,其中来自 RXBinding 的 observable 调用了对 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()
}

Hannes Dorfmann 写了一组关于使用这种方法的 MVI 模式的精彩文章。


推荐阅读