在Android中,如何通过Kotlin协程处理多个API调用
在Android开发中,如何使用Kotlin协程处理多个API调用的示例呢?假设我们已经对Kotlin协程有了一定的了解,包括定义、简单用例和示例等。现在,让我们来看一些真实的Android场景或用例。我们将从一个关于协程作用域的简单问题开始,比如生命周期作用域。
协程问题
问题 #1:如果我们在协程生命周期作用域中运行任何内容而没有提及分发器会发生什么呢?
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
val s1 = someTask()
Log.d(TAG, s1)
}
}
suspend fun someTask(): String {
// 我不相信你,给我看看当前线程
Log.d(TAG, "当前运行的线程 ${Thread.currentThread().name}")
delay(2000) // 模拟延迟以进行演示
return "任务的结果"
}
问题 #2
如果它在主线程中运行,那么…
如果我们在协程挂起函数调用之后在作用域外打印一些东西到TextView中会发生什么?
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
val s1 = someTask()
Log.d(TAG, s1)
}
binding.myTextView.text = "挂起函数调用之后"
}
问题#3
更复杂一些,我们加一个while
循环,那么结果会怎么样呢?会不会导致UI线程出现ANR呢?想要知道答案,请自行运行代码看看结果。
lifecycleScope.launch {
val s1 = someTask()
Log.d(TAG, s1)
}
binding.myTextView.text = "挂起函数调用之后"
while (true) {
//假设这是模拟UI任务,比如动画或进度旋转,正在进行的UI
//我们的主线程现在应该始终忙碌
Log.d(TAG, "我是UI模拟任务")
}
解决方案
用例 #1
我们必须进行两个API调用,第一个API调用将返回一个Auth令牌。然后我们必须使用这个令牌调用第二个API。
简而言之,我们的第二个API依赖于我们第一个API调用的结果。
lifecycleScope.launch {
val token = firstApiCall()
Log.d(TAG, "token是 $token")
val result = secondApiCall(token)
Log.d(TAG, "结果是 $result")
}
suspend fun firstApiCall(): String {
// 执行API调用1
delay(2000) // 模拟延迟以进行演示
return "1234"
}
suspend fun secondApiCall(token: String): String {
// 执行API调用2
delay(2000) // 模拟延迟以进行演示
return "API调用结果 $token"
}
就这样了?是的,就是这样!
但等等!这并不是一个真实的使用ViewModel和MVVM模式进行API调用的示例。
好的,我们来看一些真实的代码。
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class TestViewModel: ViewModel() {
fun secondApiCall() {
viewModelScope.launch {
try {
val token = getMyToken(isSuccess = true)
Log.d(TAG, "token is $token")
//Now make your second API call here using this token
//repository.getMyData(token)
} catch (ex: Exception) {
}
}
}
private suspend fun getMyToken(isSuccess: Boolean): String? {
val deferred = CompletableDeferred<String?>()
if (isSuccess) {
delay(1000)
deferred.complete("1234")
} else {
delay(1000)
deferred.completeExceptionally(Throwable("Error"))
}
return deferred.await()
}
}
不必进行两次API调用,因为第二次调用依赖于第一次调用!你可以直接调用secondApiCall()
函数,它会获取你的令牌。它会等待认证令牌返回,然后使用该令牌调用实际的API。就是这样!
用例 #2
再次,我们必须进行两次API调用,这次我们需要同时获取两个API结果。如果我们只得到一个结果,那么我们无法在UI中显示它,也无法设置RecyclerView Adapter。
lifecycleScope.launch {
//viewModel.secondApiCall()
val result = firstApiCall()
val result2 = secondApiCall()
Log.d(TAG, "$result and $result2")
}
// Assume you have some suspend functions for API calls
suspend fun firstApiCall(): String {
// Perform API call 1
delay(2000) // Simulating a delay for demonstration
return "I am first call"
}
suspend fun secondApiCall(): String {
// Perform API call 2
delay(2000) // Simulating a delay for demonstration
return "I am second call"
}
就这样?并不完全是。因为它们将按顺序运行,也就是同步运行,所以完成这两个调用将需要4秒钟。
现在我们没有任何依赖关系,那么为什么要浪费时间呢?
让我们看看最终优化的代码!
lifecycleScope.launch {
val result = async { firstApiCall() }
val result2 = async { secondApiCall() }
Log.d(TAG, "${result.await()} and ${result2.await()}")
}
现在你已经节省了2秒钟。请记住,这是每个Android开发者面试都会遇到的一个非常常见的问题。即使他们不问,你也可以提供这些示例来展示你的知识和专业能力。
感谢您的阅读!您可以关注我获取更多信息。
如果您想知道我在哪里,可以查看我的个人资料中的关于部分。