是否应为_isDataLoadingError与_isLoading创建LiveData包装类?附实现方案
为ViewModel的加载/错误状态创建包装类是否更优?
当然值得这么做!把加载状态、错误状态和数据状态封装到一个统一的包装类里,不仅能精简ViewModel里重复的LiveData声明,还能从根源上避免状态不一致的问题——比如之前可能出现“加载中”和“加载错误”同时为true的矛盾场景,用包装类就能彻底杜绝这种情况。
为什么这么做更优?
- 状态强一致性:通过密封类的排他性,同一时间只会有唯一的状态生效,不会出现逻辑矛盾。
- 代码更简洁:原来分散的
_isLoading、_isDataLoadingError、_posts三个LiveData可以合并成一个,减少冗余。 - UI层逻辑更清晰:UI只需要监听一个状态流,用分支语句就能覆盖所有场景,不用同时处理多个LiveData的回调。
具体实现方式
1. 定义状态包装密封类
首先创建一个密封类UiState,用来封装所有可能的UI状态:
sealed class UiState<out T> { // 加载中状态 object Loading : UiState<Nothing>() // 加载成功,携带数据 data class Success<out T>(val data: T) : UiState<T>() // 加载失败,可选携带错误信息 data class Error(val message: String? = null) : UiState<Nothing>() }
2. 修改ViewModel代码
把原来的三个LiveData替换成UiState类型的LiveData,并在业务逻辑中更新对应的状态:
class MainViewModel(private val schedulerProvider: BaseSchedulerProvider) : BaseViewModel() { // 用单个UiState LiveData替代原来的三个分散状态 private val _uiState = MutableLiveData<UiState<List<Post>>>() val uiState: LiveData<UiState<List<Post>>> get() = _uiState init { showPhotos() } fun showPhotos() { EspressoIdlingResource.increment() // 发起请求时,先发送加载中状态 _uiState.postValue(UiState.Loading) compositeDisposable.add(Network.items.getPhotos() .subscribeOn(schedulerProvider.io()) .observeOn(schedulerProvider.ui()) .doFinally { if (!EspressoIdlingResource.countingIdlingResource.isIdleNow) { EspressoIdlingResource.decrement() } } .subscribe({ networkPhotos -> // 获取图片成功后,继续获取帖子 showPosts(networkPhotos) }) { error -> // 获取图片失败,发送错误状态 _uiState.postValue(UiState.Error(error.localizedMessage)) Timber.e(error) }) } private fun showPosts(networkPhotos: List<NetworkPhoto>) { EspressoIdlingResource.increment() _uiState.postValue(UiState.Loading) compositeDisposable.add(Network.items.getPosts() .subscribeOn(schedulerProvider.io()) .observeOn(schedulerProvider.ui()) .doFinally { if (!EspressoIdlingResource.countingIdlingResource.isIdleNow) { EspressoIdlingResource.decrement() } } .subscribe({ networkPosts -> // 转换为领域模型后,发送成功状态并携带数据 val domainPosts = PostAndImages(networkPosts, networkPhotos).asDomaineModel() _uiState.postValue(UiState.Success(domainPosts)) }) { error -> _uiState.postValue(UiState.Error(error.localizedMessage)) Timber.e(error) }) } }
3. UI层的状态处理
在Activity/Fragment中,只需要监听uiState,用when语句处理不同状态即可:
viewModel.uiState.observe(viewLifecycleOwner) { state -> when (state) { is UiState.Loading -> { // 显示加载指示器 loadingIndicator.visibility = View.VISIBLE } is UiState.Success -> { loadingIndicator.visibility = View.GONE // 展示加载成功的数据 postAdapter.submitList(state.data) } is UiState.Error -> { loadingIndicator.visibility = View.GONE // 展示错误提示 Toast.makeText(this, state.message ?: "加载失败", Toast.LENGTH_SHORT).show() } } }
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

