Kotlin:runBlocking导致App应用出现ANR问题实例

runBlocking简介

runBlocking 是常规函数;

runBlocking 方法会阻塞当前线程来等待;

runBlocking 的主线程会一直 阻塞 直到 runBlocking 内部的协程执行完毕。

runBlocking导致App应用出现ANR问题实例的效果

点击页面上的 刷新按钮 调用 refreshByrunBlocking方法,此方法里模拟了等待30秒耗时操作,当点击 刷新按钮 等待3秒左右,点击 详情按钮 ,页面出现ANR弹框如下图所示

页面布局activity_test_anr_by_runblocking.xml代码

bash 复制代码
<?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">

    <Button
        android: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" />

    <Button
        android: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" />

    <TextView
        android: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代码

bash 复制代码
data class PsersonBean(val name: String, var moblie: String? = null)//至少有一个构造函数

TestANRByRunBlockingActivity.kt代码

bash 复制代码
package example.demo.kotlin.activity

import 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.runBlocking


class TestANRByRunBlockingActivity : Activity() {
    private lateinit var tv_detail: TextView
    private var psersonBean: PsersonBean

    init {
        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 延时初始化,这里安全起见,判断是否 isInitialized
        if (::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){//切换到主线程更新UI
                showData()
            }
        }
        LogUtil.i("调用了 refreshByGlobalScopeLaunch 方法,没有阻塞当前线程")
    }

    fun detail() {
        LogUtil.i("执行了查看详情事件")
        psersonBean.moblie = "12345678901"
        showData()
    }
}

使用GlobalScope.launch解决ANR问题

点击页面上的 刷新按钮 调用 refreshByGlobalScopeLaunch方法,此方法里模拟了等待30秒耗时操作,当点击 刷新按钮 等待3秒左右,点击 详情按钮 ,页面数据正常显示如下图所示

刷新耗时操作结束,主线程更新UI

总结

  1. runBlocking主线程会一直 阻塞 直到 runBlocking 内部的协程执行完毕,执行长时间耗时操作会导致App应用出现ANR问题。
  2. runBlocking里即使使用 async 也会导致App应用出现ANR问题。
  3. GlobalScope.launch可以解决 耗时操作App应用出现ANR问题,注意需要配合withContext(Dispatchers.Main)进行更新UI操作

推荐

Kotlin:协程基础

相关推荐
ghie90904 小时前
C#语言中使用“using“关键字的介绍
开发语言·c#
七夜zippoe4 小时前
Java性能调优工具篇:JMH基准测试与Profiler(JProfiler/Async-Profiler)使用指南
java·开发语言·jprofiler·jmh·async-profiler
從南走到北4 小时前
JAVA国际版二手车交易二手车市场系统源码支持Android+IOS+H5+APP
android·java·ios
江上清风山间明月4 小时前
Android 系统中进程和线程的区别
android·python·线程·进程
小龙报5 小时前
《嵌入式成长系列之51单片机 --- Keil5创建工程》
c语言·开发语言·c++·单片机·嵌入式硬件·51单片机·学习方法
无限进步_5 小时前
【C语言】贪吃蛇游戏设计思路深度解析:从零开始理解每个模块
c语言·开发语言·c++·git·游戏·github·visual studio
听风吟丶5 小时前
Java 函数式编程深度实战:从 Lambda 到 Stream API 的工程化落地
开发语言·python
2501_940094025 小时前
mig烧录卡资源 Mig-Switch游戏合集 烧录卡 1.75T
android·游戏·安卓·switch
rainFFrain6 小时前
qt显示类控件--- Label
开发语言·qt
渡我白衣6 小时前
深入理解 OverlayFS:用分层的方式重新组织 Linux 文件系统
android·java·linux·运维·服务器·开发语言·人工智能