在另一个协程内部时协程 launch()

pni*_*zle 4 kotlin kotlin-coroutines coroutinescope

我有以下代码(伪代码)

fun onMapReady()
{
    //do some stuff on current thread (main thread)

    //get data from server
    GlobalScope.launch(Dispatchers.IO){

        getDataFromServer { result->

            //update UI on main thread
            launch(Dispatchers.Main){
                updateUI(result) //BREAKPOINT HERE NEVER CALLED
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

正如评论所述,代码永远不会进入协程调度到主队列。但是,如果我明确使用GlobalScope.launch(Dispatchers.Main)而不是仅仅使用以下内容,则以下内容有效launch(Dispatchers.Main)

fun onMapReady()
{
    //do some stuff on current thread (main thread)

    //get data from server
    GlobalScope.launch(Dispatchers.IO){

        getDataFromServer { result->

            //update UI on main thread
            GlobalScope.launch(Dispatchers.Main){
                updateUI(result) //BREAKPOINT HERE IS CALLED
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

为什么第一种方法不起作用?

bro*_*oot 5

我认为这里的问题是getDataFromServer()异步的,它立即返回,因此您launch(Dispatchers.Main)在退出GlobalScope.launch(Dispatchers.IO) { ... }块后调用。换句话说:您尝试使用已经完成的协程范围来启动协程。

我的建议是不要将异步、基于回调的 API 与这样的协程混合在一起。协程与同步挂起函数配合使用效果最佳。另外,如果您更喜欢异步执行所有内容并独立于其他任务(您onMapReady()启动了 3 个单独的异步操作),那么我认为协程根本不是一个好的选择。

谈到你的例子:你确定你不能getDataFromServer()直接从主线程执行吗?它不应该阻塞主线程,因为它是异步的。同样,在某些库中,回调会在主线程中自动执行,在这种情况下,您的示例可以替换为:

fun onMapReady() {
    getDataFromServer { result->
        updateUI(result)
    }
}
Run Code Online (Sandbox Code Playgroud)

如果结果在后台线程中执行,那么您可以GlobalScope.launch(Dispatchers.Main)像以前一样使用,但这并不是我们使用协程的常用方式。或者您可以在 Android 上使用runOnUiThread()等实用程序,这可能更有意义。