首页 > 解决方案 > 如何在协程中制作敬酒消息?

问题描述

环境

假设我有一个创建 toast 消息的函数

fun makeToast(success: Boolean){
    if (success){
        Toast.makeText(someContext, "Success", Toast.LENGHT_SHORT).show()
    }
}

此功能用于另一个暂停的功能,例如

suspend fun makeRequest(){
    success = doSomeHTTPRequest()
    makeToast(success)
}

当我使用这个函数来执行请求时,我将在 IO 的协程中执行它,例如

CoroutineScope(IO).launch{
    makeRequest()
}

问题

像上面建议的那样做以结束:

java.lang.NullPointerException: Can't toast on a thread that has not called Looper.prepare()

问题

解决此问题的最佳做法是什么?直接的方法是更改makeToast​​为

fun makeToast(success: Boolean){
    if (success){
        Looper.prepare()
        Toast.makeText(someContext, "Success", Toast.LENGHT_SHORT).show()
        Looper.loop()
    }
}

然而,这感觉很尴尬,因为这意味着该函数makeToast已经知道它可能不会在主线程中被调用,这在我看来会产生奇怪的耦合。

标签: androidkotlinkotlin-coroutines

解决方案


Main在运行只允许从主线程调用的代码时,您需要指定使用调度程序。我想从错误消息中可以看出,如果它们有一个准备好的 Looper,你可以使用其他线程,但是如果你只是从主线程中专门制作 Toasts,它会容易得多。

此外,适当组合的挂起函数应该可以安全地从任何调度程序调用。您永远不必指定 Dispatcher 来调用挂起函数。当您调用阻塞代码并因此需要指定适当的后台调度程序时,或者调用需要在主线程上调用的函数时,应指定调度程序。

因此,您可以(IO)launch通话中删除。在您的函数中调用阻塞或主独占函数的位置指定您的调度程序makeRequest

suspend fun makeRequest(){
    val success = withContext(Dispatchers.IO) { 
        doSomeHTTPRequest()
    }
    withContext(Dispatchers.Main) {
        makeToast(success)
    }
}

推荐阅读