Android Architecture Components最佳实践:错误处理方案选型咨询
AAC错误处理最佳实践:单LiveData+包装对象才是最优解
嘿,这个问题问到点子上了!在使用Android Architecture Components(AAC)做错误处理时,很多开发者都会在「多LiveData」和「单LiveData+包装对象」之间纠结,我结合自己的项目经验和行业通用做法来给你梳理下:
先说说「多LiveData方案」的问题
如果为每种错误类型单独创建LiveData(比如noNetworkErrorLiveData、serverErrorLiveData、exceptionLiveData),看似直观,但实际用起来会踩不少坑:
- 状态分散,易不一致:UI层要同时观察多个LiveData,很容易出现「数据加载成功了,但之前的错误LiveData还没清零,UI误显示错误提示」的情况。
- UI逻辑零散:每个LiveData的回调都要单独处理,代码会变得碎片化,后期维护起来很头疼。
- 扩展性差:以后新增错误类型时,又要加新的LiveData,会让ViewModel的状态管理越来越臃肿。
更推荐的「单LiveData+包装对象」方案
这也是AAC官方和行业普遍认可的最佳实践,核心思路是用一个**密封类(Kotlin)或带状态的包装类(Java)**把「加载中、成功、各类错误」所有状态都封装起来,只暴露一个LiveData给UI层。
举个Kotlin的实现例子
用密封类来定义所有请求状态非常合适,因为它能限制状态的可能性,避免遗漏处理:
// 封装所有请求状态的密封类 sealed class Resource<out T> { // 加载中状态 object Loading : Resource<Nothing>() // 请求成功,携带返回数据 data class Success<out T>(val data: T) : Resource<T>() // 请求失败,携带错误类型、提示信息、异常对象(可选) data class Error( val errorType: ErrorType, val message: String? = null, val exception: Exception? = null ) : Resource<Nothing>() } // 定义具体的错误类型枚举 enum class ErrorType { NO_NETWORK, // 无网络错误 SERVER_RESPONSE, // 服务器返回的错误(字符串/JSON) EXCEPTION // 其他异常类错误 }
在ViewModel中怎么用?
发起请求时统一更新这个LiveData的状态:
class MyViewModel : ViewModel() { private val _dataResource = MutableLiveData<Resource<UserData>>() val dataResource: LiveData<Resource<UserData>> = _dataResource fun fetchUserData() { _dataResource.postValue(Resource.Loading) viewModelScope.launch { try { if (!NetworkUtils.isNetworkAvailable()) { _dataResource.postValue(Resource.Error(ErrorType.NO_NETWORK, "当前无网络连接")) return@launch } val response = apiService.getUserData() if (response.isSuccessful) { response.body()?.let { _dataResource.postValue(Resource.Success(it)) } ?: run { _dataResource.postValue(Resource.Error(ErrorType.SERVER_RESPONSE, "服务器返回空数据")) } } else { // 解析服务器返回的错误信息(比如JSON里的error字段) val errorMsg = parseServerError(response.errorBody()) _dataResource.postValue(Resource.Error(ErrorType.SERVER_RESPONSE, errorMsg)) } } catch (e: Exception) { _dataResource.postValue(Resource.Error(ErrorType.EXCEPTION, exception = e)) } } } }
UI层的处理逻辑
UI层只需要观察这一个LiveData,用when表达式(Kotlin)或switch(Java)统一处理所有状态,逻辑非常集中:
viewModel.dataResource.observe(viewLifecycleOwner) { resource -> when (resource) { is Resource.Loading -> { // 显示加载进度条 progressBar.visibility = View.VISIBLE } is Resource.Success -> { // 隐藏加载,显示数据 progressBar.visibility = View.GONE showUserData(resource.data) } is Resource.Error -> { progressBar.visibility = View.GONE when (resource.errorType) { ErrorType.NO_NETWORK -> showNoNetworkDialog() // 显示无网络提示+重试按钮 ErrorType.SERVER_RESPONSE -> showToast(resource.message ?: "服务器错误") ErrorType.EXCEPTION -> { showToast("出现未知错误") // 记录异常日志,方便排查 Log.e("MyViewModel", "请求失败", resource.exception) } } } } }
为什么这个方案更优?
- 状态统一:所有和请求相关的状态都封装在一个对象里,从根本上避免了状态不一致的问题。
- UI逻辑简洁:只需要处理一个LiveData的回调,代码集中易维护。
- 扩展性强:新增错误类型时,只需要在
ErrorType枚举里加值,然后在UI层的when里补充处理逻辑即可,无需修改ViewModel的LiveData结构。
补充:什么时候用多LiveData?
只有处理全局通用状态时(比如全局网络状态监听、全局登录失效提示),单独的LiveData才更合适。但针对单个业务请求的错误处理,「单LiveData+密封类包装」绝对是最优选择。
内容的提问来源于stack exchange,提问作者asf
相关产品推荐
相关产品推荐

