用于多个并行网络调用的 Kotlin Flow Zip 运算符
您必须在 android 中遇到一种情况,您希望并行进行多个网络调用以同时获取数据。因此,实现这件事的一种方法是借助 Kotlin Coroutines,这在这篇(Parallel Multiple Network calls using Kotlin Coroutines)文章中进行了解释。但是今天我们将在 Kotlin Flow 的 Zip Operator 的帮助下看到不同的方法。这种方法看起来不那么复杂、更简单、更干净,并且它带有来自流程本身的内置 API 错误处理功能。
了解 Kotlin Flow 中的 Zip 运算符
它是一个 Kotlin Flow运算符,在通过指定的函数组合两个流集合的发射后发射单个项目/值。这意味着它将当前流中的值与其他流一起压缩,并根据此指定函数的结果将它们作为每个组合的单个项目/值发出。让我们通过例子来理解。
所以,在这个例子中,让我们假设前两条黑线是两个不同的流集合,发出不同的值,通过压缩它们,我们实际上是在一个回调中同时获取这些值作为单个结果。现在,让我们看看如何将它与并行网络调用一起使用
In this example, we are following MVVM Architecture & using a Resource class that represents different UI states like Loading, Success, Error for wrapping our final data.
现在,假设我们需要在屏幕上显示一个文本“Hello Geeks”,但是我们将把这个文本分成两部分,即“Hello”和“Geeks”,与两个单独的网络调用分开。因此,我们需要这样一种方式,当两个 api 调用完成时,我们可以同时获取两个文本,这意味着两个 API 调用在一个单一且相同的回调中同时调用的结果。为了同时获取两个 API 调用的结果,我们将使用 zip运算符压缩两个 API 调用的结果,然后我们将从 zip运算符的 lambda函数将两个 API 的结果作为单个项目/文本返回这样我们就可以在 collect函数中收集它。我们假设:
getFirstText() : It will fetch the first text i.e. “Hello” from first network call
getSecondText() : It will fetch the second text i.e. “Geeks” from second network call
视图模型
让我们看看如何在 ViewModel 中准确地做到这一点:
Kotlin
class ExampleViewModel(
private val exampleRepo: ExampleRepo
) : ViewModel() {
// we'll post result in this MutableLiveData
private var _exampleText = MutableLiveData>()
// we can observe this LiveData from UI
val exampleText : LiveData> = _exampleText
init {
// Putting 'fetchBothText()' function in
// init{} block so that it'll
// automatically get called once this viewmodel
// will get initialized in activity/fragment
fetchBothText()
}
private fun fetchBothText() = viewModelScope.launch {
// posting loading state in this livedata for UI
_exampleText.postValue(Resource.Loading())
exampleRepo.getFirstText()
// Here, we're calling zip function
.zip(exampleRepo.getSecondText()) { firstText, secondText ->
// Here we're getting result of both network calls
// i.e. firstText & secondText
// Just returning both the values by simply
// adding them in this lambda
// function of zip and then we'll get this
// result in collect function
// Whatever we returns from here by specifying it,
// we can collect that in collect function
return@zip "$firstText $secondText"
}
// Making it run on Dispathers.IO
// i.e. input/output thread
.flowOn(Dispatchers.IO)
.catch { e ->
// Here, we'll get an exception if
// any failure occures in network calls
// So, we're simply posting error message
_exampleText.postValue(Resource.Error(e.toString()))
}
.collect { it ->
// Now here we can collect that value
// which we have passed in lambda
// function of zip i.e. "$firstText $secondText"
// Now simply returning result value as a single
// item/value by wrapping it in Resource.Success class
_exampleText.postValue(Resource.Success(it))
}
}
}
我们在 zip函数的帮助下并行调用 getFirstText() 和 getSecondText() api 调用,并在两个网络调用都完成时在同一个回调中获取两个网络调用的结果。在这种情况下,我们基本上是添加两个文本(即添加 'firstText' 和 'secondText' 以使其成为单个文本 Hello Geeks),然后我们只是返回这个指定的结果(即“$firstText $secondText”这将给 Hello Geeks”)从 zip运算符的 lambda函数中提取,以便我们可以在 flow 的 collect函数中收集它。然后从 collect函数中,我们简单地用 Resource.Success 包装单个结果并将其发布到可变的 livedata ( _exampleText ) 中,以便 UI 可以观察此更新并可以在屏幕上显示。
就是这样。我们已经使用 Zip运算符成功地并行运行了两个网络调用,当两个网络调用都完成时,我们在一个回调中得到了两者的结果。因此,这就是我们可以简单地使用 Zip运算符运行并行网络调用的方式,以便我们可以在同一个 & 单个回调中同时获得两者的结果。