为什么这个Kotlin Coroutine会冻结界面?

时间:2018-02-22 19:46:39

标签: android kotlin kotlin-coroutines

所以,我让这个代码在" onBindViewHolder" Recycler的适配器方法:

 launch(UI) {
            val bitmapDrawable = loadLargeBitmapDrawable()          
            imageView.setImageDrawable(bitmapDrawable)
  }

这冻结了我的应用程序几秒钟,锁定了我的主线程。

但后来我改为:

launch { // <- I removed the "UI"
            val bitmapDrawable = loadLargeBitmapDrawable()  

            launch(UI) { //Launch the UI coroutine inside the other
                imageView.setImageDrawable(bitmapDrawable)
            }      

  }

为什么会这样?协同程序的目的是在同一个线程(UI)中使事物异步吗? 有人可以解释为什么我必须在另一个协程范围内运行UI协程?

1 个答案:

答案 0 :(得分:12)

  

协同程序的目的是在同一个线程(UI)中使事物异步吗?

你认为协同程序的魔法比实际更多。如果你的loadLargeBitmapDrawable()函数不能暂停,但只是占用它的线程直到完成,Kotlin就无法做到这一点。当你说launch(UI)时,你命令在UI线程上运行该函数。

你的第二个例子是颠倒的,它在CommonPool上下文(这是默认值)中执行,然后将任务发布到UI线程;一个更自然的说法是这样的(我在我的代码中使用它,目的与你完全相同):

launch(UI) {
    val bitmapDrawable = withContext(CommonPool) {
        loadLargeBitmapDrawable() 
    }
    imageView.setImageDrawable(bitmapDrawable)
}

withContext将暂停您在UI线程上启动的协同程序,将重量级操作提交到公共线程池,然后在UI线程上恢复其结果的协同程序。现在,您可以将位图推送到imageView

在我的代码中,我不使用全局CommonPool上下文,因为它不在我的控制之下,我不知道它在做其他事情有多忙。相反,我将自己的线程池创建为顶级全局变量:

val threadPool = Executors.newCachedThreadPool().asCoroutineDispatcher()

然后我可以说withContext(threadPool) { ... }并且放心我的线程没有被我自己的任何其他代码使用。