简体   繁体   English

如何从 Kotlin 异步关闭中保存数据?

[英]How to save data from a Kotlin asynchronous closure?

I am calling an API in an asynchronous function, and would like to store the response from the callback.我在异步 function 中调用 API,并希望存储来自回调的响应。

Specifically, I am using the API category of AWS Amplify in an Android app, and would like to assign the return value to a LiveData variable in a ViewModel .具体来说,我在 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
}

I can receive the response and it gets logged correctly, but the response is not assigned to the exampleData variable, since the function returns early.我可以收到响应并正确记录,但响应未分配给exampleData变量,因为 function 提前返回。

In Android Studio, the variable exampleData is highlighted with the text:在 Android Studio 中,变量exampleData用文本突出显示:

Wrapped into a reference object to be modified when captured in a closure包装到参考 object 中,以便在闭包中捕获时进行修改

As I am not that familiar with the multithreading APIs in kotlin, I am not sure, how to block the function until the remote API returns its asynchronous response.由于我不太熟悉 kotlin 中的多线程 API,我不确定如何阻止 function 直到远程 API 返回其异步响应。

The most basic way of doing this would be with standard Java thread safety constructs.最基本的方法是使用标准的 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
}

Since this is on Android, this is probably a bad solution.由于这是在 Android 上,这可能是一个糟糕的解决方案。 I'm going to guess that getMuscleGroup is being called on the UI thread, and you do not want this method to actually block.我猜测在 UI 线程上调用了getMuscleGroup ,并且您不希望此方法实际阻塞。 The UI would freeze until the network call completes. UI 将冻结,直到网络调用完成。

The more Kotlin way of doing this would be to make the method a suspend method.这样做的更多 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())
        }
    }
}

This use Kotlin coroutines to suspend the coroutine until an answer is ready and then return the results.这使用 Kotlin 协程来暂停协程,直到准备好答案,然后返回结果。

Other options would be to use callbacks instead of return values or an observable pattern like LiveData or RxJava.其他选项是使用回调而不是返回值或 LiveData 或 RxJava 等可观察模式。

You cannot block to wait for the asynchronous result.您不能阻止等待异步结果。 At worst it can cause an Application Not Responding (ANR) error message to appear for the user, and at best makes your app look stuttery and feel unresponsive.在最坏的情况下,它可能会导致向用户显示应用程序无响应 (ANR) 错误消息,并且在最好的情况下会使您的应用程序看起来卡顿并感觉没有响应。

You can instead add a callback for this function:您可以改为为此 function 添加回调:

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) }
    )
}

And then in the spot where you call the code, you put your subsequent actions in the callback:然后在调用代码的地方,将后续操作放入回调中:

fun onMuscleGroupClicked(id: String) {
    getMuscleGroup(id) { exampleData ->
        // do something with the data after it arrives
    }
}

Coroutines are another option.协程是另一种选择。 They're nice because you don't have to nest your sequential actions in callbacks.它们很好,因为您不必将顺序操作嵌套在回调中。 To set it up, I would create a suspend extension function version of the API library query function by using suspendCancellableCoroutine .要设置它,我将使用suspendCancellableCoroutine创建 API 库query function 的挂起扩展 function 版本。 Then you can freely use it in other suspend functions in an orderly way.然后就可以有序的在其他挂起函数中自由使用了。 But you'll need to read the documentation on coroutines.但是您需要阅读有关协程的文档。 It's to much to explain here from scratch.在这里从头开始解释太多了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 Kotlin 从 RecyclerView (ArrayList) 保存数据? - How to save data from RecyclerView (ArrayList) with Kotlin? 如何将Cloud Firestore中的异步数据另存为变量? - How to save asynchronous data from Cloud Firestore as a variable? Kotlin - 如何使用数据 class 将数据从 firebase 保存到 livedata? - Kotlin - How to save data from firebase to livedata with data class? 如何在 Kotlin 中保存变量数据 - How to save variable data in Kotlin 如何保存 Kotlin 中另一个活动传递的数据? - How to save data passed from another activity in Kotlin? 转到上一个活动 kotlin 时如何保存 textview 的数据 - how to save data from textview when going to previous activity kotlin 如何使用 android Kotlin 从实时数据库中读取异步数据? - How to read asynchronous data from real-time database using android Kotlin? 如何在Kotlin中将旧数据保存到数据类中 - How to save old data in a data class in Kotlin Kotlin:如何从异步lambda中返回变量? - Kotlin: How to return a variable from within an asynchronous lambda? 如何在异步操作后正确获取上下文以将数据从片段保存到共享首选项中? - How to get context properly to save data into shared preferences from fragment after an asynchronous action?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM