如何从 Kotlin 异步闭包中保存数据?
How to save data from a Kotlin asynchronous closure?
我正在异步函数中调用 API,并希望存储回调的响应。
具体来说,我在 Android 应用程序中使用 AWS Amplify 的 API 类别,并想将 return 值分配给 [=11] 中的 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
变量,因为函数 return 早。
在 Android Studio 中,变量 exampleData
以文本突出显示:
Wrapped into a reference object to be modified when captured in a closure
因为我不太熟悉 kotlin 中的多线程 APIs,所以我不确定如何阻止该函数直到远程 API returns 的异步响应.
最基本的方法是使用标准 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 上进行的,因此这可能是一个糟糕的解决方案。我猜测 getMuscleGroup
正在 UI 线程上调用,并且您不希望此方法实际阻塞。 UI 会冻结,直到网络调用完成。
更多的 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 协程暂停协程,直到答案准备就绪,然后 return 结果。
其他选项是使用回调而不是 return 值或可观察的模式,如 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
}
}
协程是另一种选择。它们很好,因为您不必将顺序操作嵌套在回调中。要设置它,我将使用 suspendCancellableCoroutine
创建 API 库 query
函数的挂起扩展函数版本。然后就可以在其他suspend函数中自由有序的使用了。但是您需要阅读有关协程的文档。在这里从头开始解释太多了。
我正在异步函数中调用 API,并希望存储回调的响应。
具体来说,我在 Android 应用程序中使用 AWS Amplify 的 API 类别,并想将 return 值分配给 [=11] 中的 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
变量,因为函数 return 早。
在 Android Studio 中,变量 exampleData
以文本突出显示:
Wrapped into a reference object to be modified when captured in a closure
因为我不太熟悉 kotlin 中的多线程 APIs,所以我不确定如何阻止该函数直到远程 API returns 的异步响应.
最基本的方法是使用标准 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 上进行的,因此这可能是一个糟糕的解决方案。我猜测 getMuscleGroup
正在 UI 线程上调用,并且您不希望此方法实际阻塞。 UI 会冻结,直到网络调用完成。
更多的 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 协程暂停协程,直到答案准备就绪,然后 return 结果。
其他选项是使用回调而不是 return 值或可观察的模式,如 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
}
}
协程是另一种选择。它们很好,因为您不必将顺序操作嵌套在回调中。要设置它,我将使用 suspendCancellableCoroutine
创建 API 库 query
函数的挂起扩展函数版本。然后就可以在其他suspend函数中自由有序的使用了。但是您需要阅读有关协程的文档。在这里从头开始解释太多了。