我正在异步函数中调用API,并希望存储来自回调的响应。
具体地说,我正在Android应用程序中使用AWS Amplify的API类别,并希望将返回值分配给ViewModel
中的LiveData变量。
fun getMuscleGroup(id: String): ExampleData {
var exampleData = ExampleData.builder().name("").build()
Amplify.API.query(
ModelQuery.get(ExampleData::class.java, id),
{ response ->
Log.d("AmplifyApi", response.data.name)
exampleData = response.data
},
{ error -> Log.e("AmplifyApi", "Query failure", error) }
)
return exampleData
}
我可以接收到响应,并且记录正确,但是响应未分配给exampleData
变量,因为该函数会提前返回。
在Android Studio中,变量exampleData
突出显示为文本:
封装在闭包中捕获时要修改的参考对象
由于我不太熟悉kotlin中的多线程API,所以我不确定如何在远程API返回其异步响应之前阻止该功能。
最基本的方法是使用标准Java线程安全性构造。
fun getMuscleGroup(id: String): ExampleData {
var exampleData = ExampleData.builder().name("").build()
val latch = CountDownLatch(1)
Amplify.API.query(
ModelQuery.get(ExampleData::class.java, id),
{ response ->
Log.d("AmplifyApi", response.data.name)
exampleData = response.data
latch.countDown()
},
{ error ->
Log.e("AmplifyApi", "Query failure", error)
latch.countDown()
}
)
latch.await()
return exampleData
}
由于这是在Android上,这可能是一个不好的解决方案。我猜想在UI线程上正在调用getMuscleGroup
,并且您不希望此方法实际阻塞。用户界面将冻结,直到网络调用完成。
更加Kotlin的方法是将方法设为暂停方法。
suspend fun getMuscleGroup(id: String): ExampleData {
return suspendCoroutine { continuation ->
Amplify.API.query(
ModelQuery.get(ExampleData::class.java, id),
{ response ->
Log.d("AmplifyApi", response.data.name)
continuation.resume(response.data)
},
{ error ->
Log.e("AmplifyApi", "Query failure", error)
// return default data
continuation.resume(ExampleData.builder().name("").build())
}
}
}
此使用Kotlin协程暂停协程,直到准备好答案,然后返回结果。
其他选择是使用回调而不是返回值或类似LiveData或RxJava的可观察模式。
您不能阻止等待异步结果。在最坏的情况下,它可能会导致向用户显示“应用程序无响应”(ANR)错误消息,并充其量会使您的应用程序显得混乱而无响应。
您可以改为为此函数添加回调:
fun getMuscleGroup(id: String, callback: (ExampleData) -> Unit) {
var exampleData = ExampleData.builder().name("").build()
Amplify.API.query(
ModelQuery.get(ExampleData::class.java, id),
{ response ->
Log.d("AmplifyApi", response.data.name)
callback(response.data)
},
{ error -> Log.e("AmplifyApi", "Query failure", error) }
)
}
然后在调用代码的地方,将后续操作放入回调中:
fun onMuscleGroupClicked(id: String) {
getMuscleGroup(id) { exampleData ->
// do something with the data after it arrives
}
}
协程是另一种选择。它们很好,因为您不必将顺序操作嵌套在回调中。要进行设置,我将使用query
创建API库suspendCancellableCoroutine
函数的暂停扩展功能版本。然后,您可以有序地在其他暂挂函数中自由使用它。但是您需要阅读有关协程的文档。从头开始在这里解释太多。