如何在按钮点击时使用runBlocking

4

runBlocking 的目的是什么?我可以在 OnClickListener 中使用它吗? 这里我在 OnClickListener 中使用 runBlocking {}。 以下是我的代码。

 mBinding.ivAdd.setOnClickListener{
println("before" + Thread.currentThread().id)
        runBlocking {
            println("in async" + Thread.currentThread().id)

            val job = launch {
                // launch new coroutine and keep a reference to its Job
                delay(1000L)
                println("World!" + Thread.currentThread().id)
                mBinding.tvNoDataFound.text = "test"
            }
            println("Hello,")
            println("after hello" + Thread.currentThread().id)
            delay(5000)
            job.join() // wait until child coroutine completes
        }
        println("after runBlocking" + Thread.currentThread().id)
    }

它会显示类似于这样的错误:
 12-20 14:46:31.387 26915-26915/ I/System.out: before1
 12-20 14:46:31.399 26915-26915/ I/System.out: in async1
 12-20 14:46:31.405 26915-26915/ I/System.out: Hello,
 12-20 14:46:31.405 26915-26915/ I/System.out: after hello1
 12-20 14:46:32.410 26915-26937/I/System.out: World!1561
 12-20 14:46:32.422 26915-26937/ E/AndroidRuntime: FATAL EXCEPTION: 
 ForkJoinPool.commonPool-worker-2

   android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
       at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
       at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:907)
       at android.view.View.requestLayout(View.java:18728)
       at android.view.View.requestLayout(View.java:18728)
       at android.view.View.requestLayout(View.java:18728)
       at android.view.View.requestLayout(View.java:18728)
       at android.view.View.requestLayout(View.java:18728)
       at android.view.View.requestLayout(View.java:18728)
       at android.view.View.requestLayout(View.java:18728)
       at android.view.View.requestLayout(View.java:18728)
       at android.widget.TextView.checkForRelayout(TextView.java:7169)
       at android.widget.TextView.setText(TextView.java:4347)
       at android.widget.TextView.setText(TextView.java:4204)
       at android.widget.TextView.setText(TextView.java:4179)
       at MainActivity$onCreate$1$1$job$1.doResume(MainActivity.kt:164)
       at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:54)
       at kotlinx.coroutines.experimental.DispatchTask.run(CoroutineDispatcher.kt:123)
       at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1388)
       at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:251)
       at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:845)
       at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1674)
       at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1629)
       at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:108)

12-20 14:46:36.410 26915-26915/I/System.out: after runBlocking1
12-20 14:46:36.410 26915-26915/ I/Choreographer: Skipped 313 frames!  The 
application may be doing too much work on its main thread.

如果我使用launch(UI){ }而不是launch{ },输出结果如下:

12-20 14:57:11.700 27338-27338/ I/System.out: before1
12-20 14:57:11.710 27338-27338/ I/System.out: in async1
12-20 14:57:11.714 27338-27338/ I/System.out: Hello,
12-20 14:57:11.714 27338-27338/ I/System.out: after hello1
12-20 14:57:16.718 27338-27338/ I/art: Note: end time exceeds epoch: 

它没有打印来自另一个协程的“World”。

这是另一个使用 onCreate() 中的 runBlocking 的示例。

 println("before runblocking " + Thread.currentThread().id)
        runBlocking {
            println("in runblocking " + Thread.currentThread().id)

            val job = launch(UI) {

                println("In launch " + Thread.currentThread().id)

            }
            println("after launch " + Thread.currentThread().id)

        }
        println("after runBlocking " + Thread.currentThread().id)

以上代码的输出结果是:
12-20 15:58:13.253 8588-8588/? I/System.out: before runblocking 1
12-20 15:58:13.266 8588-8588/? I/System.out: in runblocking 1
12-20 15:58:13.271 8588-8588/? I/System.out: after launch 1
12-20 15:58:13.273 8588-8588/? I/System.out: after runBlocking 1
12-20 15:58:13.363 8588-8588/? I/System.out: In launch 1

正如我们所期望的那样,“after runblocking”应该是最后打印出来的,但实际上并不是。 如果我使用job.join来等待子协程完成,代码将会是:

  println("before runblocking " + Thread.currentThread().id)
        runBlocking {
            println("in runblocking " + Thread.currentThread().id)

            val job = launch(UI) {
                println("In launch " + Thread.currentThread().id)
            }
            println("after launch " + Thread.currentThread().id)
            job.join() // wait until child coroutine completes
        }
        println("after runBlocking " + Thread.currentThread().id)

以上代码的输出结果是:
12-20 16:10:43.234 9194-9194/ I/System.out: before runblocking 1
12-20 16:10:43.249 9194-9194/ I/System.out: in runblocking 1
12-20 16:10:43.253 9194-9194/ I/System.out: after launch 1

那么runblocking的预期行为是什么?我该如何使用runblocking?
1个回答

0

即使您正在阻塞代码,操作mBinding.tvNoDataFound.text = "test"仍在池工作线程上运行。

runBlocking旨在将常规阻塞代码与以挂起样式编写的库桥接,用于主函数和测试中。

在您的情况下,不应使用它。如果需要进行后台工作,则永远不应阻止UI线程。无论是使用launch还是launch(UI)都会发生这种情况。后者只是使一切都在同一个线程上运行,甚至在加入之前就已经阻止了。

因此,请尝试不要阻止UI线程,并通过launch(UI)或标准View.post {}安排稍后更新视图。


runBlocking 不是用来阻塞主线程直到所有协程完成任务吗?我仍然对 runBlocking 的执行顺序感到困惑。 - Vidhi
对我来说,它的意思是:运行新的协程并阻塞当前线程,直到其完成,可以被中断。 - tynn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接