You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在RxJava中先发送错误事件再返回缓存数据?

如何用RxJava在MVP架构中实现「请求失败时先展示错误再加载缓存」的需求?

刚好碰到过类似的场景,你的问题核心在于onErrorResumeNext会吞掉错误事件,导致Presenter收不到错误回调。先理清楚你的需求和约束,再给你两个可行的解决方案:


你的需求与约束

业务场景

  • 优先请求服务端数据,成功后用服务端数据更新视图;
  • 请求失败时(无网络、服务端错误等),先展示错误提示,再用本地缓存填充视图。

约束条件

  • 全程用RxJava实现,不额外加回调;
  • Presenter不能直接访问本地仓库,保持MVP的职责划分。

你已尝试的实现问题

你在Repo层用了onErrorResumeNext来切换到缓存流,但这个操作会直接吞噬错误事件,所以Presenter的错误回调永远不会被触发,自然没法展示错误提示。

你当前的Repo层代码

override fun getRepos(userName: String, page: Int, pageSize: Int):Observable<List<Repo>> { 
    return githubRemoteService.getReposList(userName, page, pageSize)
        .subscribeOn(schedulersProvider.ioThread())
        .flatMap { repos -> 
            val mappedRepos = remoteResponseMapper.mapRepoResponse(repos)
            githubLocalService.saveRepos(mappedRepos)
            Observable.just(mappedRepos)
        }
        .onErrorResumeNext(Observable.just(githubLocalService.getReposList(userName, page, pageSize)))
        .observeOn(schedulersProvider.mainThread()) 
}

Presenter层代码

githubInteractor.getRepos(userName, page, pageSize).subscribe(
    { repos -> showReposInView(repos) },
    { error -> digestAndShowErrorInView(error) // 这里永远不会被执行 }
)

最优解决方案:自定义事件封装错误+缓存数据

RxJava的流规则是:要么发onNext事件,要么发onError事件,不能同时发。所以我们需要把「错误信息」和「缓存数据」打包成一个自定义事件,通过onNext发送给Presenter,这样就能同时拿到两者。

步骤1:定义密封类包装结果状态

用Kotlin的密封类(Sealed Class)来定义两种结果类型,非常适合这种有限的状态场景:

sealed class RepoResult {
    // 请求成功的状态,携带服务端数据
    data class Success(val repos: List<Repo>) : RepoResult()
    // 请求失败的状态,携带错误信息和缓存数据
    data class ErrorWithCache(val error: Throwable, val cachedRepos: List<Repo>) : RepoResult()
}

步骤2:修改Repo层代码,返回包装后的Observable

把原来返回Observable<List<Repo>>改成返回Observable<RepoResult>,在错误分支里把错误和缓存打包:

override fun getRepos(userName: String, page: Int, pageSize: Int): Observable<RepoResult> {
    return githubRemoteService.getReposList(userName, page, pageSize)
        .subscribeOn(schedulersProvider.ioThread())
        .flatMap { repos ->
            // 服务端请求成功:保存数据,返回Success事件
            val mappedRepos = remoteResponseMapper.mapRepoResponse(repos)
            githubLocalService.saveRepos(mappedRepos)
            Observable.just(RepoResult.Success(mappedRepos))
        }
        .onErrorResumeNext { error ->
            // 请求失败:获取缓存,打包成ErrorWithCache事件
            val cachedRepos = githubLocalService.getReposList(userName, page, pageSize)
            Observable.just(RepoResult.ErrorWithCache(error, cachedRepos))
        }
        .observeOn(schedulersProvider.mainThread())
}

步骤3:Presenter层处理不同状态

订阅的时候通过when判断状态,分别处理成功、错误+缓存的场景:

githubInteractor.getRepos(userName, page, pageSize).subscribe { result ->
    when(result) {
        is RepoResult.Success -> showReposInView(result.repos)
        is RepoResult.ErrorWithCache -> {
            // 先展示错误提示
            digestAndShowErrorInView(result.error)
            // 再加载缓存数据
            showReposInView(result.cachedRepos)
        }
    }
}

这个方案完全符合你的约束:

  • 全程用RxJava的流处理,没有额外回调;
  • Presenter完全不接触本地仓库,只和Interactor返回的RepoResult打交道;
  • 清晰分离了不同状态的处理逻辑,符合MVP的职责划分。

另一种不太推荐的方案(仅供参考)

如果不想定义密封类,可以尝试用doOnError在错误发生时先触发提示,但这个方案有个致命问题:doOnError里如果要调用视图方法,会让Repo层直接依赖视图,违反了MVP的设计原则,所以不推荐:

override fun getRepos(userName: String, page: Int, pageSize: Int): Observable<List<Repo>> {
    return githubRemoteService.getReposList(userName, page, pageSize)
        .subscribeOn(schedulersProvider.ioThread())
        .flatMap { repos ->
            val mappedRepos = remoteResponseMapper.mapRepoResponse(repos)
            githubLocalService.saveRepos(mappedRepos)
            Observable.just(mappedRepos)
        }
        .doOnError { error ->
            // 切换到主线程展示错误,但这里直接调用视图方法会耦合Repo和View
            Observable.just(error)
                .observeOn(schedulersProvider.mainThread())
                .subscribe { err -> view?.showError(err) }
        }
        .onErrorResumeNext {
            Observable.just(githubLocalService.getReposList(userName, page, pageSize))
                .observeOn(schedulersProvider.mainThread())
        }
}

内容的提问来源于stack exchange,提问作者Amir Ziarati

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:04:54