使用 ViewModel、LiveData 和 RxJava 在 recyclerview 中处理数据和加载指示器的正确方法
Posted
技术标签:
【中文标题】使用 ViewModel、LiveData 和 RxJava 在 recyclerview 中处理数据和加载指示器的正确方法【英文标题】:Correct way of handling data and loading indicator in recyclerview using ViewModel, LiveData and RxJava 【发布时间】:2018-10-20 15:00:45 【问题描述】:从数据源搜索项目时,我有以下 UI 流程:
-
从源检索时显示进度指示器 -> 将 livedata 分配给
Outcome.loading(true)
显示结果 -> 分配 LiveData Outcome.success(results)
隐藏进度指示器 -> 分配 LiveData Outcome.loading(false)
现在的问题是当应用程序在后台时调用#2 和#3。恢复应用程序时,LiveData 观察者只被通知 #3 而不是 #2 导致未填充的 RecyclerView。
处理这种情况的正确方法是什么?
class SearchViewModel @Inject constructor(
private val dataSource: MusicInfoRepositoryInterface,
private val scheduler: Scheduler,
private val disposables: CompositeDisposable) : ViewModel()
private val searchOutcome = MutableLiveData<Outcome<List<MusicInfo>>>()
val searchOutcomLiveData: LiveData<Outcome<List<MusicInfo>>>
get() = searchOutcome
fun search(searchText: String)
Timber.d(".loadMusicInfos")
if(searchText.isBlank())
return
dataSource.search(searchText)
.observeOn(scheduler.mainThread())
.startWith(Outcome.loading(true))
.onErrorReturn throwable -> Outcome.failure(throwable)
.doOnTerminate searchOutcome.value = Outcome.loading(false)
.subscribeWith(object : DisposableSubscriber<Outcome<List<MusicInfo>>>()
override fun onNext(outcome: Outcome<List<MusicInfo>>?)
searchOutcome.value = outcome
override fun onError(e: Throwable)
Timber.d(e, ".onError")
override fun onComplete()
Timber.d(".onComplete")
).addTo(disposables)
override fun onCleared()
Timber.d(".onCleared")
super.onCleared()
disposables.clear()
下面是我的结果类
sealed class Outcome<T>
data class Progress<T>(var loading: Boolean) : Outcome<T>()
data class Success<T>(var data: T) : Outcome<T>()
data class Failure<T>(val e: Throwable) : Outcome<T>()
companion object
fun <T> loading(isLoading: Boolean): Outcome<T> = Progress(isLoading)
fun <T> success(data: T): Outcome<T> = Success(data)
fun <T> failure(e: Throwable): Outcome<T> = Failure(e)
【问题讨论】:
【参考方案1】:您不应将加载状态设置为“双重”状态(真/假)。 您的进度状态应仅在加载时发送,然后您将进入成功或失败状态。最后永远不要回到加载状态。这样做你总是知道你的视图需要显示哪个状态。
如果加载 -> 显示加载器 如果成功 -> 隐藏加载器,显示数据 如果错误 -> 隐藏加载器,显示错误这是我的android Conductor + MVVM + Dagger project template 的示例摘录,它使用导体,但您可以将导体控制器替换为片段或活动,这是相同的逻辑。
sealed class DataRequestState<T>
class Start<T> : DataRequestState<T>()
class Success<T>(var data: T) : DataRequestState<T>()
class Error<T>(val error: Throwable) : DataRequestState<T>()
视图模型:
@ControllerScope
class HomeControllerViewModel
@Inject
constructor(homeRepositoryManager: HomeRepositoryManager) : BaseControllerViewModel(),
DataFetchViewModel<Home>
private val _dataFetchObservable: DataRequestLiveData<Home> =
DataRequestLiveData(homeRepositoryManager.home())
override val dataFetchObservable: LiveData<DataRequestState<Home>> = _dataFetchObservable
override fun refreshData()
_dataFetchObservable.refresh()
基础数据控制器(片段/活动/导体):
abstract class BaseDataFetchController<VM, D> :
BaseViewModelController<VM>() where VM : BaseControllerViewModel, VM : DataFetchViewModel<D>
override fun onViewCreated(view: View)
super.onViewCreated(view)
viewModel.dataFetchObservable.observe(this, Observer
it?.let
when (it)
is DataRequestState.Start -> dataFetchStart()
is DataRequestState.Success ->
dataFetchSuccess(it.data)
dataFetchTerminate()
is DataRequestState.Error ->
dataFetchError(it.error)
dataFetchTerminate()
)
protected abstract fun dataFetchStart()
protected abstract fun dataFetchSuccess(data: D)
protected abstract fun dataFetchError(throwable: Throwable)
【讨论】:
感谢您的回答。一个代码 sn-p 真的很有帮助。【参考方案2】:加载状态和加载数据应该严格分开,你应该维护两个实时数据和两个观察者。
这样,loading == false
,您将收到有关重新订阅的最新数据。
想一想:加载状态并不是真正的结果。
【讨论】:
谢谢。更好的类名是 State。我在几个 github 存储库中看到了这种模式,但有些没有很好地处理进度指示器。不确定仅出于进度指示器的目的而使用额外的 livedata 对象是否有效,但这可能是一种解决方法。 优化正确性,而不是最小化 MutableLiveData imo 的数量以上是关于使用 ViewModel、LiveData 和 RxJava 在 recyclerview 中处理数据和加载指示器的正确方法的主要内容,如果未能解决你的问题,请参考以下文章
JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )
使用 ViewModel 和 LiveData 多次改造执行 API