Android进阶知识:ANR的定位与解决

1、前言

ANR对于Android开发者来说一定不会陌生,从刚开始学习Android时的一不注意就ANR,到后来知道主线程不能进行耗时操作注意到这点后,程序出现ANR的情况就大大减少了,甚至于消失了。那么真的是只要在主线程做耗时操作就会产生ANR吗?为什么在有时候明明觉得自己没在主线程做耗时操作也出现了ANR呢?一旦出现莫名其妙的ANR,怎么定位导致ANR的产生的位置和解决问题呢?那么接下来就来一个个的解决这些问题。

2、ANR是什么?

ANR全称Application Not Responding即应用程序无响应。在Android中如果应用程序有一段时间无法响应用户操作,系统会弹出弹窗,让用户选择是继续等待还是强制关闭程序。一款良好应用APP是不应该出现这个弹窗的。

3、ANR的产生原因

ANR产生原因和类型有以下几种:

1、Activity在5秒钟之内无法响应屏幕触摸事件挥着键盘输入事件就会产生ANR

KeyDispatchTimeout
Reason:Input event dispatching timed out

2、BroadcastReceiver在10秒钟之内还未执行完成就会产生ANR

BroadcastTimeout
Reason:Timeout of broadcast BroadcastRecord

3、Service各个生命周期在20秒钟之内没有执行完成就会产生ANR

ServiceTimeout
Reason:Timeout executing service

4、ContentProvider在10秒钟之内没有执行完成就会产生ANR

ContentProviderTimeout
Reason:timeout publishing content providers

在以上这几种原因中出现最多的一般是第一种,而且往往都是因为在写代码时不注意,在主线程做了耗时的操作。

4、ANR的定位与解决

关于ANR的定位这里举一个例子来看。这是我之前遇到的一次出现ANR的时候所解决问题的情况和解决步骤。

 

首先当然是复现ANR现象,找准ANR出现的地方,查看对应代码,如果能直接看出来问题所在,找到代码中做的错误操作那么直接修改相应代码就解决问题了。但是如果没法轻易看出问题原因,接下来就只好去Logcat中查看对应的错误日志。

07-22 21:39:17.019 819-851/? E/ActivityManager: ANR in com.xxxx.performance (com.xxxx.performance/.view.home.activity.MainActivity)PID: 7398Reason: Input dispatching timed out (com.xxxx.performance/com.xxxx.performance.view.home.activity.MainActivity, Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 29.  Wait queue head age: 8579.3ms.)Load: 18.22 / 18.1 / 18.18CPU usage from 0ms to 8653ms later:124% 7398/com.xxxx.performance: 118% user + 6.5% kernel / faults: 4962 minor 7 major82% 819/system_server: 28% user + 53% kernel / faults: 10555 minor 11 major23% 4402/adbd: 1% user + 22% kernel10% 996/com.android.systemui: 4.6% user + 6.2% kernel / faults: 4677 minor 1 major4.6% 2215/com.android.phone: 1.5% user + 3.1% kernel / faults: 5411 minor6.3% 6268/perfd: 3.4% user + 2.8% kernel / faults: 134 minor0.5% 1149/com.miui.whetstone: 0.1% user + 0.3% kernel / faults: 3016 minor 1 major0.2% 2097/com.xiaomi.finddevice: 0.1% user + 0.1% kernel / faults: 2256 minor0.6% 2143/com.miui.daemon: 0.2% user + 0.3% kernel / faults: 2798 minor1.2% 1076/com.xiaomi.xmsf: 0.6% user + 0.6% kernel / faults: 2802 minor0% 2122/com.android.server.telecom: 0% user + 0% kernel / faults: 2929 minor0% 2244/com.miui.contentcatcher: 0% user + 0% kernel / faults: 1800 minor0% 2267/com.mediatek.nlpservice: 0% user + 0% kernel / faults: 2052 minor0% 2166/com.xiaomi.mitunes: 0% user + 0% kernel / faults: 1797 minor 3 major0% 2190/com.fingerprints.service: 0% user + 0% kernel / faults: 1857 minor0.1% 154/mmcqd/0: 0% user + 0.1% kernel0.4% 8069/logcat: 0.3% user + 0.1% kernel......

从日志第一行开始看,可以看到发生错误的应用包名和类名,这里是ANR in com.xxxx.performance (com.xxxx.performance/.view.home.activity.MainActivity)。接着看到进程号PID7398。发生ANRReasonInput dispatching timed out就是上面提到的第一种。再往下就是活跃进程的CPU占用率日志。

   124% 7398/com.xxxx.performance82% 819/system_server10% 996/com.android.systemui4.6% 2215/com.android.phone......

光看Logcat中的日志只能看到这些信息,大概知道是在MainActivity出现了问题,但还是不能清楚的定位到发生ANR的代码行,想要获得进一步的错误信息只能通过查看ANR过程中生成的堆栈信息文件traces.txt了。

traces.txt文件位置在/data/anr/目录下,可以通过以下adb命令将其拷贝到sd卡目录下获取查看。

adb shell
cat  /data/anr/traces.txt  >/mnt/sdcard/traces.txt  
exit

traces.txt里的信息:

DALVIK THREADS (42):
"main" prio=5 tid=1 Native| group="main" sCount=1 dsCount=0 obj=0x75ceafb8 self=0x55933ae7e0| sysTid=7398 nice=0 cgrp=default sched=0/0 handle=0x7f7ddae0f0| state=S schedstat=( 101485399944 3411372871 31344 ) utm=9936 stm=212 core=1 HZ=100| stack=0x7fc8d40000-0x7fc8d42000 stackSize=8MB| held mutexes=kernel: __switch_to+0x74/0x8ckernel: futex_wait_queue_me+0xcc/0x158kernel: futex_wait+0x120/0x20ckernel: do_futex+0x184/0xa48kernel: SyS_futex+0x88/0x19ckernel: cpu_switch_to+0x48/0x4cnative: #00 pc 00017750  /system/lib64/libc.so (syscall+28)native: #01 pc 000d1584  /system/lib64/libart.so (_ZN3art17ConditionVariable4WaitEPNS_6ThreadE+140)native: #02 pc 00388098  /system/lib64/libart.so (_ZN3artL12GoToRunnableEPNS_6ThreadE+1068)native: #03 pc 000a5db8  /system/lib64/libart.so (_ZN3art12JniMethodEndEjPNS_6ThreadE+24)native: #04 pc 000280e4  /data/dalvik-cache/arm64/system@framework@boot.oat (Java_android_graphics_Paint_native_1init__+156)at android.graphics.Paint.native_init(Native method)at android.graphics.Paint.<init>(Paint.java:435)at android.graphics.Paint.<init>(Paint.java:425)at android.text.TextPaint.<init>(TextPaint.java:49)at android.text.Layout.<init>(Layout.java:160)at android.text.StaticLayout.<init>(StaticLayout.java:111)at android.text.StaticLayout.<init>(StaticLayout.java:87)at android.text.StaticLayout.<init>(StaticLayout.java:66)at android.widget.TextView.makeSingleLayout(TextView.java:6543)at android.widget.TextView.makeNewLayout(TextView.java:6383)at android.widget.TextView.checkForRelayout(TextView.java:7096)at android.widget.TextView.setText(TextView.java:4082)at android.widget.TextView.setText(TextView.java:3940)at android.widget.TextView.setText(TextView.java:3915)at com.xxxx.performance.view.home.fragment.AttendanceCheckInFragment.onNowTimeSuccess(AttendanceCheckInFragment.java:887)at com.xxxx.performance.presenter.attendance.AttendanceFragmentPresenter$6.onNext(AttendanceFragmentPresenter.java:214)at com.xxxx.performance.presenter.attendance.AttendanceFragmentPresenter$6.onNext(AttendanceFragmentPresenter.java:205)at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:198)at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:250)at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)............

还是从头开始看,来看每个字段对应的含义:
线程名:main
线程优先级:prio=5
线程锁ID: tid=1
线程状态:Native 线程组名称:group="main"
线程被挂起的次数:sCount=1
线程被调试器挂起的次数:dsCount=0
线程的java的对象地址:obj=0x75ceafb8
线程本身的Native对象地址:self=0x55933ae7e0
线程调度信息:
Linux系统中内核线程ID: sysTid=7398与主线程的进程号相同
线程调度优先级:nice=0
线程调度组:cgrp=default
线程调度策略和优先级:sched=0/0
线程处理函数地址:handle=0x7f7ddae0f0
线程的上下文信息:
线程调度状态:state=S
线程在CPU中的执行时间、线程等待时间、线程执行的时间片长度:schedstat=(101485399944 3411372871 31344 )
线程在用户态中的调度时间值:utm=9936
线程在内核态中的调度时间值:stm=212
最后执行这个线程的CPU核序号:core=1
线程的堆栈信息:
堆栈地址和大小:stack=0x7fc8d40000-0x7fc8d42000 stackSize=8MB
最后看到堆栈信息里的这一行:

at com.xxxx.performance.view.home.fragment.AttendanceCheckInFragment.onNowTimeSuccess(AttendanceCheckInFragment.java:887)

这里就看清楚了是在AttendanceCheckInFragment中的887行出现的问题,再到对应代码行中就很容易发现ANR的原因了。

5、ANR的相关问题

  • 在Activity的onCreate方法里调用sleep方法会发生ANR吗?

以前一直认为在主线程做了耗时操作就会发生ANR,那么真的是这样吗?在ActivityonCreate方法里调用Thread.sleep(60 * 1000)让主线程sleep60秒,会导致应用程序ANR吗?写个Demo测试一下。

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);try {Log.d("ANR","开始sleep");Thread.sleep(60*1000);Log.d("ANR","sleep完成");} catch (InterruptedException e) {e.printStackTrace();}}
}

如上代码,运行程序,结果应用没有发生ANR,在sleep了60秒后正常打印日志。

 

再次运行程序,这回在程序运行后按下返回键查看现象:

 

这次果然就ANR了。通过这个例子,显而易见的得到了这个问题的正确答案。在ActivityonCreate方法里调用sleep方法或者说做耗时操作,不一定会产生ANR。其实从ANR本身意为应用程序没有响应,同时根据上面总结的ANR原因就可以看出,耗时操作本身是不会产生ANR的,导致ANR的根本还是应用程序无法在一定时间内响应用户的操作。所以因为主线程被耗时操作占用了,主线成程无法对下一个操作进行响应才会ANR,没有需要响应的操作自然就不会产生ANR,或者应该这样说:主线程做耗时操作,非常容易引发ANR

6、总结

  • 光在主线程做耗时操作不会产生ANR,超时响应用户操作才会产生ANR。
  • ANR的定位方法主要是根据Logcat中日志和ANR过程中生成的堆栈信息文件traces.txt。
  • 解决问题不如预防问题,写代码的时候要注意预防产生ANR。
  • 预防ANR的产生不光是在Activity中注意要把耗时操作放到子线程中去,还要注意在使用其他三个组件时,在其生命周期中同样不能做太耗时的操作。另外在使用多线程时候要注意同步和死锁的情况,一旦产生死锁主线程同样会引发ANR。

作者:胖宅老鼠
链接:https://juejin.cn/post/6844904069731975176
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/162064.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ 的网页界面操作说明

启动 上面给用户添加了角色和权限&#xff0c; 我们就可以登录了 先手动创建两个队列&#xff0c;然后再把这两个队列和交换机绑定&#xff0c;就可以发布消息 回到队列中看看有什么变化 队列中显示绑定了交换机 再看一下队列中发生的变化 可以看到队列中收到了信息

基于C#实现树状数组

有一种数据结构是神奇的&#xff0c;神秘的&#xff0c;它展现了位运算与数组结合的神奇魅力&#xff0c;太牛逼的&#xff0c;它就是树状数组&#xff0c;这种数据结构不是神人是发现不了的。 一、概序 假如我现在有个需求&#xff0c;就是要频繁的求数组的前 n 项和&#x…

Redis事务的理解与使用

文章目录 Redis 事务1)基本认识2)事务操作1.MULTI2.EXEC3.错误处理4.DISCARD5.WATCH6.SCRIPT Redis 事务 官方文档&#xff0c;永远是你学习的第一手资料&#xff1a;Redis 事务 1)基本认识 谈到事务&#xff0c;大家首先都会联想到 mysql 中复杂但又功能强大的“事务”&…

SpringBoot——感谢尚硅谷官方文档

SpringBoot——感谢尚硅谷官方文档 1 Spring与SpringBoot1、Spring能做什么1.1、Spring的能力1.2、Spring的生态1.3、Spring5重大升级1.3.1、响应式编程1.3.2、内部源码设计 2、为什么用SpringBoot2.1、SpringBoot优点2.2、SpringBoot缺点 3、时代背景3.1、微服务3.2、分布式分…

Dubbo从入门到上天系列第十八篇:Dubbo引入Zookeeper等注册中心简介以及DubboAdmin简要介绍,为后续详解Dubbo各种注册中心做铺垫!

文章目录 一&#xff1a;Dubbo注册中心引言 1&#xff1a;什么是Dubbo的注册中心&#xff1f; 2&#xff1a;注册中心关系图解 3&#xff1a;引入注册中心服务执行流程 4&#xff1a;Dubbo注册中心好处 5&#xff1a;注册中心核心作用 二&#xff1a;注册中心实现方案 …

10.docker的网络network-概述

1.docker的网络模式 docker共有四种网路模式&#xff0c;分别是bridge、host、none和container. 1.1 bridge bridge,也称为虚拟网桥。在bridge模式下&#xff0c;为每个容器分配、配置IP等&#xff0c;并将容器连接到一个docker0。使用–network bridge命令指定&#xff0c;…

C# Winform使用log4net记录日志

写在前面 Log4Net是从Java的log4j移植过来的&#xff0c;功能也与log4j类似&#xff0c;可以把日志信息输出到文件、数据库、控制台、Windows 事件日志、远程系统日志服务等不同的介质或目标。 Log4Net配置选项丰富灵活&#xff0c;并且可在运行时动态更新配置并应用&#xf…

解析紫光展锐T820 5G芯片——让照片接近原色

紫光展锐系统级安全的高性能5G SoC芯片平台T820&#xff0c;采用八核CPU架构&#xff0c;6nm EUV先进工艺&#xff0c;金融级全内置安全方案&#xff0c;在性能、功耗与5G通信体验等方面&#xff0c;较上一代产品更为出色。 此前&#xff0c;已经为大家讲解过T820的拍照、安全性…

mac跑分工具 Geekbench v6.2.2

Geekbench 6 是一款跨平台的系统性能测试软件&#xff0c;可以对处理器和内存等硬件进行评测&#xff0c;并提供了单核和多核两种测试模式。该软件适用于 Windows、macOS、Linux 和 iOS 等多种操作系统平台。 Geekbench 6 测试可以帮助用户快速准确地了解自己设备的性能表现&am…

PHP中间件实现

目录 1、简单中间实现 2、使用闭包函数实现中间件 在PHP中&#xff0c;中间件是一种常用的设计模式&#xff0c;用于处理请求和响应&#xff0c;它可以在请求到达目标处理程序之前或响应发送给客户端之前执行一些特定的逻辑。中间件提供了一种灵活的方式来修改或扩展应用程序的…

查看当前laravel版本三种方法(笔记二)

1、在终端中使用 Artisan 命令&#xff1a;在 Laravel 项目的根目录下&#xff0c;打开终端&#xff08;命令行界面&#xff09;&#xff0c;然后运行以下命令&#xff1a; php artisan --version 2、控制器中打印版本 var_dump(app()->version()); 3、在 Laravel 项目的根目…

【kubernetes】k8s架构之节点

文章目录 1、集群架构示意图2、概述3、管理3.1 节点名称唯一性3.2 节点自注册3.3 手动节点管理 4、节点状态4.1 地址&#xff08;Addresses&#xff09;4.2 状况&#xff08;Condition&#xff09;4.3 容量&#xff08;Capacity&#xff09;与可分配&#xff08;Allocatable&am…

PTA-输出三角形面积和周长

本题要求编写程序&#xff0c;根据输入的三角形的三条边a、b、c&#xff0c;计算并输出面积和周长。注意&#xff1a;在一个三角形中&#xff0c; 任意两边之和大于第三边。三角形面积计算公式&#xff1a;areas(s−a)(s−b)(s−c)​&#xff0c;其中s(abc)/2。 输入格式&…

某60区块链安全之Call函数簇滥用实战二学习记录

区块链安全 文章目录 区块链安全Call函数簇滥用实战二实验目的实验环境实验原理实验内容实验步骤EXP利用 Call函数簇滥用实战二 实验目的 学会使用python3的web3模块 学会并区分以太坊call、staticcall、delegatecall三种函数调用的特点 找到合约漏洞进行分析并形成利用 实验…

03梯度下降

目录 lambda基础知识 代码 核心算法&#xff1a; lambda基础知识 lambda 是 Python 中的一个关键字&#xff0c;用于创建匿名函数。匿名函数是一种没有具体名称的小型、临时的函数&#xff0c;通常用于一次性的、简单的操作。lambda 函数的语法如下&#xff1a;python Copy c…

计算3个点的6种分布在平面上的占比

假设平面的尺寸是6*6&#xff0c;用11的方式构造2&#xff0c;在用21的方式构造3 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 3 3 3 x 3 3 2 2 2 1 2 2 2 2 2 1 2 2 在平面上有一个点x&#xff0c;11的操作吧平面分成了3部分2a1&#xff0c;2a…

海康Visionmaster-模块索引:MFC 模块索引异常解决 办法

现象&#xff1a;文件编码格式为 UTF-8 不带签名编码格式&#xff0c;模块索引会出现 模块无法找到异常 更改文件类型为 UTF-8 带签名格式或 vs 默认 GBK2312 编码格式

JMeter处理接口签名sign

写接口脚本的时候&#xff0c;很多接口涉及到签名&#xff0c;今天介绍下用JMeter编写签名脚本的方法。 举个例子&#xff0c;开启红包接口&#xff0c;请求方式为post POST /v1/api/red/open json请求参数 { "red_id":1, "timestamp":"1667033841…

2023年中国边缘计算网关现状及发展趋势分析[图]

边缘计算网关是一种可以在设备上运行本地计算、消息通信、数据缓存等功能的工业智能网关&#xff0c;可以在无需联网的情况下实现设备的本地联动以及数据处理分析。边缘计算网关是一种连接物联网设备和云端服务的关键技术&#xff0c;它可以在设备和云端之间建立一个安全、高效…

实例讲解Simulink的MATLAB Function模块

内容 MATLAB Function是一个支持使用M语言编写模块功能,并能够将所编写的M语言生成C代码&#xff0c;用于开发桌面应用和嵌入式应用的模块。它支持的 MATLAB内建函数比 Fcn模块要广泛&#xff0c;除去基本的四则运算、逻辑操作符和关系操作符&#xff0c;还可以调用MATLAB各种…