3

从数据源搜索项目时,我有以下 UI 流程:

  1. 从源检索时显示进度指示器 -> 将 livedata 分配给Outcome.loading(true)
  2. 显示结果 -> 分配 LiveDataOutcome.success(results)
  3. 隐藏进度指示器 -> 分配 LiveDataOutcome.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)
    }
}
4

2 回答 2

6

您不应将加载状态设为“双重”状态(真/假)。您的进度状态应仅在加载时分派,然后您进入成功或失败状态。最后永远不要回到加载状态。这样做你总是知道你的视图需要显示哪个状态。

  • 如果加载 -> 显示加载器
  • 如果成功 -> 隐藏加载器,显示数据
  • 如果错误 -> 隐藏加载程序,则显示错误

这是我的Android Conductor + MVVM + Dagger 项目模板的示例摘录,它使用了导体,但您可以用片段或活动替换导体控制器,这是相同的逻辑。

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)
}
于 2018-05-10T11:05:56.780 回答
2

加载状态和加载数据应该严格分开,你应该维护两个实时数据和两个观察者。

这样,loading == false您将收到有关重新订阅的最新数据。

想一想:加载状态并不是真正的结果。

于 2018-05-10T12:38:28.250 回答