Android使用 Combine 和 Stateflow同时调用多个API
在这里,我解释了如何将多个 API(或任何其他数据提供者)组合在一起以在 Android 应用程序中实现复杂的页面。
在当前的现代 Android 应用程序中,有时我们应该显示来自多个数据源的数据,所有数据的状态都很重要。
在本文中,我将 MVVM、Koin 和存储库模式与协程规则结合使用。
想象一下,我们想要设计一个页面来显示来自 API 1 和 API 2 的数据列表。它们应该组合起来显示数据,当每个数据都来自不同的存储库时,它会变得更加复杂。
或者即使每次数据发生变化,我们应该如何更新我们的页面?
假设我们有 3 个名为exchangeOverview
、rollingPrice
和watchList
的 API 调用,但最后我们只需要一个数据,我们将其称为fullList
。
private val exchangeOverView = MutableStateFlow<Result<ExchangeOverview>>(Result.Loading)
private val rollingPrice =
MutableStateFlow<Result<HashMap<String, RollingPrice>>?>(Result.Loading)
private val watchList = MutableStateFlow<Result<List<Coin>>?>(Result.Loading)
结果类如下所示
sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T?) : Result<T>()
data class Error(val exception: String, val errorCode: Int) : Result<Nothing>()
object Loading : Result<Nothing>()
}
对于Stateflow
的默认部分,我们使用Result.Loading
,因为我们没有任何缓存(如果需要我们可以把它作为Stateflow
的默认值)。
为什么不使用 Sharedflow
?
因为我们需要每个 API 的最后状态,并且我们想在多个新/旧页面中使用它,因此我们使用 Stateflow
而不是 Sharedflow
。
我们不知道所有 API 何时都成功,但我们知道希望所有 API 都准备就绪。
就是这样!这里我们的函数看起来像这样:
private suspend fun getExchangeOverview() {
exchangeRepository.getExchangeOverview().collect { result ->
when (result) {
is Result.Loading -> {
exchangeOverView.emit(Result.Loading)
}
is Result.Success -> {
exchangeOverView.emit(Result.Success(result.data))
}
is Result.Error -> {
exchangeOverView.emit(Result.Error(result.exception, result.errorCode))
}
}
}
}
对于所有其他 API,我们使用的功能相同。
为了组合它们,我们使用下面的函数,当所有 API 都成功时我们只发出成功,并且 fof
失败和加载我们有自定义功能。
private fun combineData(): StateFlow<Result<List<SymbolsItem>>?>? =
combine(
exchangeOverView,
rollingPrice,
watchList
) { exchangeOverView, rollingPrice, wattchList ->
if (exchangeOverView is Result.Success && rollingPrice is Result.Success && wattchList is Result.Success && currentUserState.value) {
val data = ourFunctionForUseAllDataToghether()
Result.Success(data)
} else if (exchangeOverView is Result.Error) {
Result.Error(exchangeOverView.exception, exchangeOverView.errorCode)
} else if (rollingPrice is Result.Error) {
Result.Error(rollingPrice.exception, rollingPrice.errorCode)
} else {
Result.Loading
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
_fullDataStateFlow = combineData()
好的,我们的数据可以使用了。
在 Fragment/Activity 中我们调用了:
lifecycleScope.launchWhenResumed {
viewModel.fullListData.collectLatest { result ->
when (result) {
is Result.Loading -> {
shwoLoading()
}
is Result.Success -> {
showData()
}
is Result.Error -> {
showError()
}
}
}
}
参考
https://medium.com/@keyvan.nrz/call-multile-api-calls-with-combine-and-stateflow-1cf4ea4ac322