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


    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">
    
        <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    实体类:PsersonBean.kt代码

    data class PsersonBean(val name: String, var moblie: String? = null)//至少有一个构造函数
    
    • 1

    TestANRByRunBlockingActivity.kt代码

    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()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    使用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:协程基础

  • 相关阅读:
    web前端-javascript-立即执行函数(说明、例子)
    Redis 的 Java 客户端(Jedis、SpringDataRedis、SpringCache、Redisson)基本操作指南
    2023-09-09青少年软件编程(C语言)等级考试试卷(二级)解析
    数据治理-基本概念
    个人数学建模算法库之图的最短路径模型
    ln函数的导数
    cpu 支持内存带宽与内存最大长度的关系《鸟哥的 Linux 私房菜》
    21-30==c++知识点
    C++的缺陷和思考(一)
    TikTok与心灵成长:娱乐与启发并重
  • 原文地址:https://blog.csdn.net/ChinaDragon10/article/details/136736445