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

RxJava Android:如何在onError()返回自定义ErrorBase而非Throwable

如何让RxJava的doOnError返回自定义ErrorBase对象?

嘿,作为RxJava新手,你想把错误处理逻辑封装到自定义的ErrorBase类里,这个思路非常棒——能避免在各个地方重复写Throwable判断的代码。不过首先得纠正一个小误区:doOnError是个副作用操作符,它不能返回任何值,它只是在错误发生时执行一些额外操作(比如打日志),没法直接把Throwable换成ErrorBase返回给下游。

咱们分两种方案来实现你的需求,看哪种更适合你的项目:


方案一:直接在ViewModel中处理Throwable为ErrorBase(快速简单)

这种方式不需要修改Observable的类型,只需要在ViewModel的错误回调里创建ErrorBase实例,直接用它处理好的信息更新UI就行:

修改LoginViewModel中的onError方法:

override fun onError(e: Throwable) {
    val errorBase = ErrorBase(e)
    // 直接使用ErrorBase处理后的提示信息
    loginError.postValue(errorBase.message)
    // 如果需要状态码,还可以新增一个LiveData来保存
    // loginErrorCode.postValue(errorBase.statusCode)
    loginLoader.postValue(false)
}

同时,你可以在LoginRepository的doOnError里做一些副作用操作(比如打印错误日志),但注意这里不能返回值:

fun getLoginDetails(auth: String): Observable<LoginResponseModel> {
    return apiInterface.getLoginDetails(auth)
        .doOnNext { 
            // 处理成功的副作用,比如缓存数据
        }
        .doOnError { throwable ->
            // 这里可以打印日志、上报错误等,只是执行操作,不能返回值
            Log.e("LoginRepository", "Login failed", throwable)
        }
}

方案二:用密封类统一封装结果(推荐大型项目)

这种方式更符合响应式编程的思想,把成功和错误都包装成统一的Result类型,让ViewModel只需要处理一种事件流,代码更清晰易维护:

第一步:创建密封类Result

用来统一包装成功和错误的结果:

sealed class Result<out T> {
    // 成功时携带返回数据
    data class Success<out T>(val data: T) : Result<T>()
    // 错误时携带自定义的ErrorBase
    data class Error(val error: ErrorBase) : Result<Nothing>()
}

第二步:修改LoginRepository的方法

把原来返回Observable<LoginResponseModel>改成返回Observable<Result<LoginResponseModel>>,用onErrorResumeNext把Throwable转换成ErrorBase并包装:

class LoginRepository @Inject constructor(private val apiInterface: ApiInterface, val utils: Utils) {
    fun getLoginDetails(auth: String): Observable<Result<LoginResponseModel>> {
        return apiInterface.getLoginDetails(auth)
            .map { loginResponse ->
                // 成功时包装为Success类型
                Result.Success(loginResponse)
            }
            .onErrorResumeNext { throwable ->
                // 错误时创建ErrorBase,包装为Error类型
                Observable.just(Result.Error(ErrorBase(throwable)))
            }
    }
}

第三步:修改LoginViewModel的Observer

适配新的Result类型,在onNext里判断是成功还是错误:

class LoginViewModel @Inject constructor(
    private val loginRepository: LoginRepository
) : ViewModel() {
    // 原有LiveData保持不变
    private val TAG = this.javaClass.name
    private var loginResult: MutableLiveData<LoginResponseModel> = MutableLiveData()
    private var loginError: MutableLiveData<String> = MutableLiveData()
    private var loginLoader: MutableLiveData<Boolean> = MutableLiveData()
    // 修改Observer的类型为Result<LoginResponseModel>
    private lateinit var disposableObserver: DisposableObserver<Result<LoginResponseModel>>

    // 原有getter方法保持不变
    fun loginResult(): LiveData<LoginResponseModel> { return loginResult }
    fun loginError(): LiveData<String> { return loginError }
    fun loginLoader(): LiveData<Boolean> { return loginLoader }

    private fun getLoginData(auth: String) {
        loginLoader.postValue(true)
        initLoginObserver()
        loginRepository.getLoginDetails(auth)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .debounce(400, MILLISECONDS)
            .subscribe(disposableObserver)
    }

    private fun initLoginObserver() {
        disposableObserver = object : DisposableObserver<Result<LoginResponseModel>>() {
            override fun onComplete() { }

            override fun onNext(result: Result<LoginResponseModel>) {
                loginLoader.postValue(false)
                when(result) {
                    is Result.Success -> {
                        // 处理成功数据
                        loginResult.postValue(result.data)
                    }
                    is Result.Error -> {
                        // 处理自定义错误
                        loginError.postValue(result.error.message)
                        // 如果需要状态码,可以使用result.error.statusCode
                    }
                }
            }

            override fun onError(e: Throwable) {
                // 这里理论上不会被调用,因为onErrorResumeNext已经把错误转换成Result.Error
                // 做个兜底处理
                loginLoader.postValue(false)
                loginError.postValue("Unexpected error occurred")
            }
        }
    }

    // 其他方法保持不变
    fun disposeElements() {
        if (null != disposableObserver && !disposableObserver.isDisposed) 
            disposableObserver.dispose()
    }
    fun loginClicked() { getLoginData("auth") }
}

两种方案对比

  • 方案一:快速简单,适合小型项目或快速迭代,不需要修改太多核心代码,直接在ViewModel中处理错误。
  • 方案二:更规范,错误处理逻辑集中在Repository层,ViewModel只需要处理统一的Result类型,代码更清晰,便于后续扩展和维护,适合大型项目。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:18:30