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.hqwc.cn/news/206985.html

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

相关文章

10年开发工程师总结,8大主流程序员兼职平台,月入30k不是梦!

今年互联网行业陆续裁员减薪&#xff0c;许多人怨声载道的同时也开始另谋出路。而对于程序员更是应该提早做好准备&#xff0c;活跃在兼职接单的最前沿。 我们程序员是一门技术工种&#xff0c;与互联网其他行业相比薪水会相对高一点&#xff0c;不过钱也不是那么好赚的&#…

引爆关注,聚焦上海新闻媒体邀请

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 上海拥有众多的新闻媒体机构&#xff0c;包括报纸、电视、广播和网络媒体等。这些媒体在报道国内外新闻、传播信息等方面发挥着重要作用。 其中&#xff0c;上海电视台是上海最大的电视…

猫不长肉怎么办?增肥效果好、让猫咪迅速圆润起来的猫罐头分享!

秋冬来临&#xff0c;北方的猫咪有暖气还好过一些&#xff0c;南方的猫咪只能依靠自己的抵抗力来过冬。如果不囤点脂肪&#xff0c;怕冷的小猫咪要怎么过冬啊&#xff1f;有些猫咪无论怎么吃也吃不胖&#xff0c;真的让铲屎官很烦恼。想起我新手养猫的那段日子为了给我家猫咪增…

数字化转型:传统门店突破困境,实现可持续发展的必由之路

自2023年疫情管控基本解除以来&#xff0c;人民群众体验线下消费的意愿充分释放&#xff0c;夜经济、文娱文旅消费、暑期经济等线下消费场景持续走热。据统计&#xff0c;今年1-7月份&#xff0c;我国实体店零售额同比增长4.2%。虽然实体经济出现了消费复苏&#xff0c;发展向好…

CTF靶场搭建及Web赛题制作与终端docker环境部署

♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ ♡ ♥ 写在前面 ╔═══════════════════════════════════════════════════…

深入理解 synchronized 原理

目录 一. 前言 二. Java对象的内存布局 2.1. 对象头 2.2. Mark Word 2.3. Class Metadata Pointer 2.4. Length 三. 偏向锁 3.1. 偏向锁的工作流程 3.2. 偏向失效 3.2.1. 误区一 3.3. 偏向撤销 3.3.1. 误区一 3.4. 偏向撤销的底层实现 3.5. HashCode与偏向撤销 …

前端入门(二)Vue2基本语法、样式渲染、数据代理与监测

文章目录 Vue简介Vue的特点Hello, Vue Vue基本语法模板语法数据绑定&#xff08;v-bind、v-model&#xff09;el与data的两种写法 事件处理&#xff08;v-on:click / click&#xff09;事件修饰符键盘事件&#xff08;缺&#xff09; 计算属性与监视&#xff08;computed、watc…

Python生成exe文件

Python如何生成exe文件 在终端执行 pip install pyinstaller 在终端执行pyinstaller E:\fund_data\GetFund.py&#xff0c;运行结束后会在D:\Python\Python311\Scripts\dist\目录下生成GetFund.exe文件 3.双击exe文件运行&#xff0c;如果未出现预期结果&#xff0c;可以把e…

邦永PM2项目管理系统 SQL注入漏洞复现

0x01 产品简介 邦永PM2项目管理系统科学地将项目管理思想和方法和谐、统一&#xff0c;使得长期以来困扰项目管理工作者的工期、进度、投资和成本情况无法整体动态管理的问题得到了全面而彻底的解决。 0x02 漏洞概述 邦永科技PM2项目管理平台Global_UserLogin.aspx接口处未对用…

QT搭建的Ros/librviz的GUI软件

1.前言 开发初期学习了下面博主的文章&#xff0c;也报了他在古月局的课&#xff0c;相当于感谢吧。 ROS Qt5 librviz人机交互界面开发一&#xff08;配置QT环境&#xff09;-CSDN博客​​​​​​​r 软件前期也是参考他的开源项目 GitHub - chengyangkj/Ros_Qt5_Gui_App …

拆解现货黄金隔夜利息计算公式

在讨论现货黄金投资手续费的时候&#xff0c;隔夜利息是经常被忽略的一个方面&#xff0c;但它是投资者不得不考虑的成本因素&#xff0c;特别是在中长线交易的情况下。隔夜利息是根据投资者的持仓数量和交易方向所计算出的利息&#xff0c;如果投资者需要持仓过夜&#xff0c;…

APP自动化之Poco框架

今天给大家介绍一款自动化测试框架Poco&#xff0c;其脚本写法非常简洁、高效&#xff0c;其元素定位器效率更快&#xff0c;其本质基于python的第三方库&#xff0c;调试起来也会非常方便&#xff0c;能够很好的提升自动化测试效率&#xff0c;节省时间。 (一&#xff09;背景…