如何观察多个LiveData的状态?
How to observe the states of multiple LiveData?
我正在尝试观察页面的加载状态。但是,我对我的 ViewModel 进行了 2 API 次调用。我想在加载两个项目之前显示进度条。
我有一个密封的class来表示数据的加载状态,它是这样的:
sealed class DataState<out R> {
data class Success<out T>(val data: T) : DataState<T>()
data class Error(val exception: Exception) : DataState<Nothing>()
object Loading : DataState<Nothing>()
}
我的视图模型:
init {
getData1()
getData2()
}
val data1 = MutableLiveData<List<model1>>()
val data2 = MutableLiveData<List<model2>>()
private fun getData1() {
viewModelScope.launch {
data1.postValue(DataState.Loading)
val result = try {
repository.getData1().data!!
}
catch (e: Exception) {
data1.postValue(DataState.Error(e))
return@launch
}
data1.postValue(DataState.Success(result))
}
}
private fun getData2() {
viewModelScope.launch {
data2.postValue(DataState.Loading)
val result = try {
repository.getData2().data!!
}
catch (e: Exception) {
data2.postValue(DataState.Error(e))
return@launch
}
data2.postValue(DataState.Success(result))
}
}
我想观察一个实时数据,这样我就可以看到两种状态都是成功的。这可能吗?
你可能想要 MediatorLiveData
:
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
这是文档中的示例 - 这是一个非常简单的示例,当源 LiveDatas 的 任一 发布新值时,仅在 liveDataMerger
上设置一个值。
an example on the Android Developers blog 更接近您的要求:
...
result.addSource(liveData1) { value ->
result.value = combineLatestData(liveData1, liveData2)
}
result.addSource(liveData2) { value ->
result.value = combineLatestData(liveData1, liveData2)
}
...
private fun combineLatestData(
onlineTimeResult: LiveData<Long>,
checkinsResult: LiveData<CheckinsResult>
): UserDataResult {
val onlineTime = onlineTimeResult.value
val checkins = checkinsResult.value
// Don't send a success until we have both results
if (onlineTime == null || checkins == null) {
return UserDataLoading()
}
// TODO: Check for errors and return UserDataError if any.
return UserDataSuccess(timeOnline = onlineTime, checkins = checkins)
}
因此,每次您在其中一个 LiveData 上获得新值时,都会将它们都传递给一个进行一些验证的函数和 returns 当前状态。
我还应该指出 Flow
s are recommended for a lot of things now,并且 combine
函数(与 MediatorLiveData
做同样的事情)更容易一些阅读(那篇文章中的#5)。只是让你知道!这里哪个都好
您不需要为每个 API 调用单独设置 LiveData
个 DataState
。
而不是这个:
val data1 = MutableLiveData<List<model1>>()
val data2 = MutableLiveData<List<model2>>()
你可以拥有这个:
private val _stateLiveData = MutableLiveData<DataStates<model3>>()
val statesLiveData: LiveData<DataStates<model3>>
get() = _stateLiveData
将 model3
视为聚合了 model1
和 model2
的 data class
。可以用来聚合其他数据字段,以后可以用到。
data class model3(
var data1: model1?,
var data2: model2?
)
这样,当你观察状态时,维护状态变得更简单:
viewModel.statesLiveData.observe(this) { states ->
when(states) {
is DataStates.Error -> // Handle Error here.
DataStates.Loading -> // Handle Loading here.
is DataStates.Success -> // Handle Success here.
}
}
还有一点,由于方法 getData1()
和 getData2()
分别启动协程,因此 REST API 调用不是按顺序排列的。并且不能保证哪个调用会先完成,这可能会导致额外的代码只是为了保持进度直到两个调用都完成。
这可以通过对 REST API 调用本身进行排序来轻松解决,方法是将数据获取操作合并到一个 CoroutineScope
.
下的一个方法中
总结:
class YourViewModel : ViewModel() {
private val _stateLiveData = MutableLiveData<DataStates<model3>>()
val statesLiveData: LiveData<DataStates<model3>>
get() = _stateLiveData
init {
getData()
}
private fun getData() {
viewModelScope.launch(Dispatchers.IO) {
notifyState(DataStates.Loading)
try {
val result1 = repository.getData1().data!!
val result2 = repository.getData2().data!!
notifyState(
DataStates.Success(
model3(
result1,
result2
)
)
)
} catch (e: Exception) {
notifyState(DataStates.Error(e))
}
}
}
private fun notifyState(state: DataStates<model3>) {
viewModelScope.launch(Dispatchers.Main) {
_stateLiveData.value = state
}
}
}
我正在尝试观察页面的加载状态。但是,我对我的 ViewModel 进行了 2 API 次调用。我想在加载两个项目之前显示进度条。
我有一个密封的class来表示数据的加载状态,它是这样的:
sealed class DataState<out R> {
data class Success<out T>(val data: T) : DataState<T>()
data class Error(val exception: Exception) : DataState<Nothing>()
object Loading : DataState<Nothing>()
}
我的视图模型:
init {
getData1()
getData2()
}
val data1 = MutableLiveData<List<model1>>()
val data2 = MutableLiveData<List<model2>>()
private fun getData1() {
viewModelScope.launch {
data1.postValue(DataState.Loading)
val result = try {
repository.getData1().data!!
}
catch (e: Exception) {
data1.postValue(DataState.Error(e))
return@launch
}
data1.postValue(DataState.Success(result))
}
}
private fun getData2() {
viewModelScope.launch {
data2.postValue(DataState.Loading)
val result = try {
repository.getData2().data!!
}
catch (e: Exception) {
data2.postValue(DataState.Error(e))
return@launch
}
data2.postValue(DataState.Success(result))
}
}
我想观察一个实时数据,这样我就可以看到两种状态都是成功的。这可能吗?
你可能想要 MediatorLiveData
:
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
这是文档中的示例 - 这是一个非常简单的示例,当源 LiveDatas 的 任一 发布新值时,仅在 liveDataMerger
上设置一个值。
an example on the Android Developers blog 更接近您的要求:
...
result.addSource(liveData1) { value ->
result.value = combineLatestData(liveData1, liveData2)
}
result.addSource(liveData2) { value ->
result.value = combineLatestData(liveData1, liveData2)
}
...
private fun combineLatestData(
onlineTimeResult: LiveData<Long>,
checkinsResult: LiveData<CheckinsResult>
): UserDataResult {
val onlineTime = onlineTimeResult.value
val checkins = checkinsResult.value
// Don't send a success until we have both results
if (onlineTime == null || checkins == null) {
return UserDataLoading()
}
// TODO: Check for errors and return UserDataError if any.
return UserDataSuccess(timeOnline = onlineTime, checkins = checkins)
}
因此,每次您在其中一个 LiveData 上获得新值时,都会将它们都传递给一个进行一些验证的函数和 returns 当前状态。
我还应该指出 Flow
s are recommended for a lot of things now,并且 combine
函数(与 MediatorLiveData
做同样的事情)更容易一些阅读(那篇文章中的#5)。只是让你知道!这里哪个都好
您不需要为每个 API 调用单独设置 LiveData
个 DataState
。
而不是这个:
val data1 = MutableLiveData<List<model1>>()
val data2 = MutableLiveData<List<model2>>()
你可以拥有这个:
private val _stateLiveData = MutableLiveData<DataStates<model3>>()
val statesLiveData: LiveData<DataStates<model3>>
get() = _stateLiveData
将 model3
视为聚合了 model1
和 model2
的 data class
。可以用来聚合其他数据字段,以后可以用到。
data class model3(
var data1: model1?,
var data2: model2?
)
这样,当你观察状态时,维护状态变得更简单:
viewModel.statesLiveData.observe(this) { states ->
when(states) {
is DataStates.Error -> // Handle Error here.
DataStates.Loading -> // Handle Loading here.
is DataStates.Success -> // Handle Success here.
}
}
还有一点,由于方法 getData1()
和 getData2()
分别启动协程,因此 REST API 调用不是按顺序排列的。并且不能保证哪个调用会先完成,这可能会导致额外的代码只是为了保持进度直到两个调用都完成。
这可以通过对 REST API 调用本身进行排序来轻松解决,方法是将数据获取操作合并到一个 CoroutineScope
.
总结:
class YourViewModel : ViewModel() {
private val _stateLiveData = MutableLiveData<DataStates<model3>>()
val statesLiveData: LiveData<DataStates<model3>>
get() = _stateLiveData
init {
getData()
}
private fun getData() {
viewModelScope.launch(Dispatchers.IO) {
notifyState(DataStates.Loading)
try {
val result1 = repository.getData1().data!!
val result2 = repository.getData2().data!!
notifyState(
DataStates.Success(
model3(
result1,
result2
)
)
)
} catch (e: Exception) {
notifyState(DataStates.Error(e))
}
}
}
private fun notifyState(state: DataStates<model3>) {
viewModelScope.launch(Dispatchers.Main) {
_stateLiveData.value = state
}
}
}