runBlocking简介
runBlocking 是常规函数;
runBlocking 方法会阻塞当前线程来等待;
runBlocking 的主线程会一直 阻塞 直到 runBlocking 内部的协程执行完毕。
runBlocking导致App应用出现ANR问题实例的效果
点击页面上的 刷新按钮 调用 refreshByrunBlocking方法,此方法里模拟了等待30秒耗时操作,当点击 刷新按钮 等待3秒左右,点击 详情按钮,页面出现ANR弹框如下图所示
页面布局activity_test_anr_by_runblocking.xml代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/btn_refresh"android:layout_width="match_parent"android:layout_height="50dp"android:layout_marginLeft="20dp"android:layout_marginTop="60dp"android:layout_marginRight="20dp"android:gravity="center"android:onClick="onClick"android:text="刷新"android:textColor="@color/black"android:textSize="20sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/btn_detail"android:layout_width="0dp"android:layout_height="50dp"android:layout_marginTop="60dp"android:gravity="center"android:onClick="onClick"android:text="详情"android:textColor="@color/black"android:textSize="20sp"app:layout_constraintLeft_toLeftOf="@+id/btn_refresh"app:layout_constraintRight_toRightOf="@+id/btn_refresh"app:layout_constraintTop_toBottomOf="@+id/btn_refresh" /><TextViewandroid:id="@+id/tv_detail"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginTop="60dp"android:onClick="onClick"android:textColor="@color/black"android:textSize="16sp"app:layout_constraintLeft_toLeftOf="@+id/btn_detail"app:layout_constraintRight_toRightOf="@+id/btn_detail"app:layout_constraintTop_toBottomOf="@+id/btn_detail"tools:text="用户信息" /></androidx.constraintlayout.widget.ConstraintLayout>
实体类:PsersonBean.kt代码
data class PsersonBean(val name: String, var moblie: String? = null)//至少有一个构造函数
TestANRByRunBlockingActivity.kt代码
package example.demo.kotlin.activityimport android.app.Activity
import android.os.Bundle
import android.view.View
import android.widget.TextView
import example.demo.kotlin.R
import example.demo.kotlin.bean.PsersonBean
import example.demo.kotlin.utils.LogUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlockingclass TestANRByRunBlockingActivity : Activity() {private lateinit var tv_detail: TextViewprivate var psersonBean: PsersonBeaninit {psersonBean = PsersonBean("测试用户01")}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_test_anr_by_runblocking)initView()}override fun onStart() {super.onStart()showData()}fun initView() {tv_detail = findViewById(R.id.tv_detail)}fun showData() {//使用lateinit var 延时初始化,这里安全起见,判断是否 isInitializedif (::tv_detail.isInitialized) {tv_detail.setText("$psersonBean")//注意,因为PsersonBean是 data class 类型 不需要重新toString 函数}}fun onClick(view: View) {when (view.id) {R.id.btn_refresh -> refreshByrunBlocking2()R.id.btn_detail -> detail()}}/*** 出现了ANR问题*/fun refreshByrunBlocking() {runBlocking(context = Dispatchers.IO) {LogUtil.i("开始执行 刷新 耗时操作了")delay(30000)//假设30秒,可以假设真实网络请求出现超时了,方便演示出现ANR问题psersonBean = PsersonBean("测试用户02")LogUtil.i("刷新 耗时操作结束")}LogUtil.i("刷新 耗时操作 事件执行完毕")showData()}/*** runBlocking里即使使用 async 也会出现ANR问题*/fun refreshByrunBlocking2() {runBlocking(context = Dispatchers.IO) {LogUtil.i("开始执行 刷新 耗时操作了")val psersonBean = async {delay(30000)//假设30秒,可以假设真实网络请求出现超时了,方便演示出现ANR问题PsersonBean("测试用户02")}LogUtil.i("刷新 耗时操作结束")}LogUtil.i("刷新 耗时操作 事件执行完毕")showData()}/*** 使用GlobalScope.launch ,没有出现ANR问题*/fun refreshByGlobalScopeLaunch() {GlobalScope.launch(context = Dispatchers.IO) {LogUtil.i("开始执行 刷新 耗时操作了")delay(30000)//假设30秒,可以假设真实网络请求出现超时了,方便演示出现ANR问题psersonBean = PsersonBean("测试用户02")LogUtil.i("刷新 耗时操作结束")withContext(Dispatchers.Main){//切换到主线程更新UIshowData()}}LogUtil.i("调用了 refreshByGlobalScopeLaunch 方法,没有阻塞当前线程")}fun detail() {LogUtil.i("执行了查看详情事件")psersonBean.moblie = "12345678901"showData()}
}
使用GlobalScope.launch解决ANR问题
点击页面上的 刷新按钮 调用 refreshByGlobalScopeLaunch方法,此方法里模拟了等待30秒耗时操作,当点击 刷新按钮 等待3秒左右,点击 详情按钮,页面数据正常显示如下图所示
刷新耗时操作结束,主线程更新UI
总结
- runBlocking主线程会一直 阻塞 直到 runBlocking 内部的协程执行完毕,执行长时间耗时操作会导致App应用出现ANR问题。
- runBlocking里即使使用 async 也会导致App应用出现ANR问题。
- GlobalScope.launch可以解决耗时操作App应用出现ANR问题,注意需要配合withContext(Dispatchers.Main)进行更新UI操作
推荐
Kotlin:协程基础