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
相关产品推荐
相关产品推荐

