04.ANR监控设计实践
目录介绍
- 01.ANR整体概述
- 1.1 项目背景介绍
- 1.2 遇到问题
- 1.3 基础概念介绍
- 1.4 设计目标
- 1.5 产生收益分析
- 02.ANR基础分析
- 2.1 究竟什么是ANR
- 2.2 ANR产生条件
- 2.3 ANR触发场景
- 2.4 四大组件ANR
- 03.ANR流程分析
- 3.1 触发ANR的步骤
- 3.2 Service的ANR分析
- 3.3 ANR后系统执行流程
- 3.4 那些极端情况不会ANR
- 3.6 ANR静默和弹窗
- 3.7 处理ANR信息收集
- 3.8 dump进程stack
- 04.ANR是怎么设计
- 4.1 什么是输入系统
- 4.2 系统进程输入系统
- 4.3 组件类型ANR
- 4.4 Input类型ANR
- 4.5 事件分发异步机制
- 05.ANR监控排查思路
- 5.1 ANR监控整体思路
- 5.2 目前流行方案
- 5.3 如何查看ANR日志
- 5.4 导出ANR信息
- 5.5 ANR具体如何分析
- 06.其他设计说明
- 6.1 性能设计
- 6.2 稳定性设计
- 6.3 灰度设计
- 6.4 降级设计
- 6.5 异常设计
- 07.其他说明介绍
- 7.1 参考链接
01.ANR整体概述
1.1 项目背景介绍
1.2 遇到问题
1.3 基础概念介绍
- ANR-WatchDog 单独起一个子线程向主线程发送一个重置变量操作的Runnable,然后休眠一段时间(自定义的ANR超时时间),如果休眠后该变量的值没有被重置,则表明有ANR发生。
1.4 设计目标
1.5 产生收益分析
02.ANR基础分析
2.1 究竟什么是ANR
- ANR的简单理解
- ANR Activity not responding(页面没有响应) ;ANR Application not responding 应用没有响应
- Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。
2.2 ANR产生条件
- ANR的产生需要满足三个条件
- 主线程:只有应用程序进程的主线程响应超时才会产生ANR;
- 超时时间:产生ANR的上下文不同,超时时间也会不同,但只要在这个时间上限内没有响应就会ANR;
- 输入事件/特定操作:输入事件是指按键、触屏等设备输入事件,特定操作是指BroadcastReceiver和Service的生命周期中的各个函数,产生ANR的上下文不同,导致ANR的原因也会不同;
- 造成ANR的原因一般有两种:
- 当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
- 当前的事件正在处理,但没有及时完成
2.3 ANR触发场景
- 分别有哪些场景
- 主线程,被阻塞5秒钟以上,就会抛出ANR对话框。不同的组件发生ANR的时间不一样,主要是四大组件。
- 点击事件(按键和触摸事件)5s内没被处理: Input event dispatching timed out
- 那些操作会引发ANR
- 1.Activity,Fragment中暴力相应点击事件有可能会导致ANR;
- 2.断点调试时,程序可能会出现ANR无限应;
- 3.主线程做了耗时操作,比如查询数据库数据导致ANR
2.4 四大组件ANR
- service 前台20s后台200s未完成启动: Timeout executing service
- Service Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。
- 对于Service有两类: 对于前台服务,则超时为SERVICE_TIMEOUT = 20s;对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT = 200s
- BroadcastReceiver的事件(onReceive方法)在规定时间内没处理完:Timeout of broadcast BroadcastRecord
- 对于前台广播,则超时为BROADCAST_FG_TIMEOUT = 10s;对于后台广播,则超时为BROADCAST_BG_TIMEOUT = 60s
- 以BroadcastReviver为例,在onReceive()方法执行10秒内没发生第一种ANR(也就是在这个过程中没有输入事件或输入事件还没到5s)才会发生Receiver timeout,否则将先发生事件无相应ANR,所以onReceive()是有可能执行不到10s就发生ANR的,所以不要在onReceive()方法里面干活
- ContentProvider
- publish在10s内没进行完:timeout publishing content providers
- Activity 界面如果5秒未响应
- Activity not responding。这种是特别常见的场景
- 发生原因
- 主线程有耗时操作
- 复杂布局
- IO操作
- 被子线程同步锁block
- 被Binder对端block
- Binder被占满导致主线程无法和SystemServer通信
- 得不到系统资源(CPU/RAM/IO)
- 从进程角度看发生原因有:
- 当前进程:主线程本身耗时或者主线程的消息队列存在耗时操作、主线程被本进程的其它子线程所blocked
- 远端进程:binder call、socket通信
03.ANR流程分析
3.1 触发ANR的步骤
- 四大组件触发ANR的步骤。
- 大概是:埋下注册超时 ----> 触发超时 ----> 引发超时ANR
3.2 Service的ANR分析
- 埋下注册超时
- 调用startService,在Service进程attach到system_server进程的过程中会调用
realStartServiceLocked()
方法来注册超时 - ----> ActiveServices 类 realStartServiceLocked 方法
- ----> ActiveServices 类 bumpServiceExecutingLocked 方法
- ----> ActiveServices 类 scheduleServiceTimeoutLocked 方法 , handler发送延迟消息
- ----> ActivityManagerService 类的 MainHandler 中,有处理handler延迟消息,具体看:SERVICE_TIMEOUT_MSG
- 调用startService,在Service进程attach到system_server进程的过程中会调用
- 触发超时,触发超时分析
- 在system_server进程
AS.realStartServiceLocked()
调用的过程会埋下注册超时, 超时没有启动完成则会超时。 - 那么什么时候会触发超时的引线呢? 经过Binder等层层调用进入目标进程的主线程handleCreateService()的过程。
- ----> ActivityThread 类 handleCreateService 方法
- ----> ActivityManagerService 类 serviceDoneExecuting 方法
- ----> ActiveServices 类 serviceDoneExecutingLocked 方法
- 在system_server进程
- 引发超时ANR,触发超时分析
- 介绍了埋下注册超时和触发超时的过程, 如果在超时倒计时结束之前成功拆卸注册超时,那么就没有引发ANR的机会。
- 但总有些极端情况下无法即时拆除注册超时,导致触发超时了, 其结果就是App发生ANR。
- 在system_server进程中有一个Handler线程, 名叫”ActivityManager”。当倒计时结束便会向该Handler线程发送 一条信息SERVICE_TIMEOUT_MSG
- ----> ActivityManagerService 类 handleMessage 方法
- ----> ActiveServices 类 serviceTimeout 方法
3.3 ANR后系统执行流程
- ANR后流程如下
- APP发生ANR
- 进程接收异常终止信号,开始写入进程ANR信息(当时场景,包含当前线程所有堆栈信息、CPU/IO的使用情况等);
- 弹出ANR提示框,提示用户关闭APP或者继续等待;(不同ROM表现不同,有的手机厂商会去掉这个提示框)
- appNotResponding介绍
- 无论ANR的来源是哪里,最终都会走到ProcessRecord中的appNotResponding,这个方法包括了ANR的主要流程。
3.4 那些极端情况不会ANR
- 具体可以看appNotResponding方法中的下面代码。会发现有一些条件直接执行了return。有哪些极端的情况不会ANR
- 一长串if else,给出了几种比较极端的情况,会直接return,而不会产生一个ANR。
- 这些情况包括:进程正在处于正在关闭的状态,正在crash的状态,被kill的状态,或者相同进程已经处在ANR的流程中。
3.5 ANR静默和弹窗
- 看下面的源码分析,还是appNotResponding方法。
- 大概的意思是,通过isSilentAnr()判断是否是静默ANR,如果是那么则直接kill杀死app。其实就是后台ANR。
- 如果是前台,则是通过handler发送一个消息,创建anr弹窗对象,赋值给message的obj对象。
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, WindowProcessController parentProcess, boolean aboveSystem, String annotation) { synchronized (mService) { if (isSilentAnr() && !isDebugging()) { kill("bg anr", true); return; } makeAppNotRespondingLocked(activityShortComponentName, annotation != null ? "ANR " + annotation : "ANR", info.toString()); if (mService.mUiHandler != null) { Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem); mService.mUiHandler.sendMessage(msg); } } }
- 那么究竟是怎么判断是后台服务呢?
- 除非另有配置,否则在后台进程中吞下anr并杀死进程。非私有访问仅用于测试。
boolean isSilentAnr() { return !getShowBackground() && !isInterestingForBackgroundTraces(); }
- 再来看看处理弹窗展示的逻辑,具体看ActivityManagerService类的handleMessage方法。what是:SHOW_NOT_RESPONDING_UI_MSG
final class UiHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case SHOW_NOT_RESPONDING_UI_MSG: { mAppErrors.handleShowAnrUi(msg); ensureBootCompleted(); } } }
3.7 处理ANR信息收集
- 为了方便定位,因此需要收集ANR信息。下面代码还是
appNotResponding
方法,主要是收集信息的重要代码。- dump很多信息到ANR Trace文件里,下面的逻辑就是选择需要dump的进程。
- ANR Trace文件是包含许多进程的Trace信息的,因为产生ANR的原因有可能是其他的进程抢占了太多资源
- 具体看:
AMS.appNotResponding
流程代码- 输出ANR Reason信息到EventLog. 也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息。
- 收集并输出重要进程列表中的各个线程的traces信息,该方法较耗时。
- 输出当前各个进程的CPU使用情况以及CPU负载情况。
- 将traces文件和CPU使用情况信息保存到dropbox,即data/system/dropbox目录(ANR信息最为重要的信息)。
- 根据进程类型,来决定直接后台杀掉,还是弹框告知用户。
- 选择需要dump的进程的逻辑需要大概说下
- 需要被dump的进程被分为了firstPids、nativePids以及extraPids三类。
- 拿到需要dump的所有进程的pid后,AMS开始按照firstPids、nativePids、extraPids的顺序dump这些进程的堆栈
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids, (isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids, nativePids);
3.8 dump进程stack
- 具体看:
AMS.dumpStackTraces
流程代码 - 1、收集firstPids进程的stacks:
- 第一个是发生ANR进程;第二个是system_server;其余的是mLruProcesses中所有的persistent进程。
- 2、收集Native进程的stacks。(dumpNativeBacktraceToFile)
- 依次是mediaserver,sdcard,surfaceflinger进程。
- 3、收集lastPids进程的stacks:
- 依次输出CPU使用率top 5的进程;
04.ANR是怎么设计
4.1 什么是输入系统
ANR
机制到底是什么,其背后的原理究竟如何,为什么要设计出这样的机制?这些问题时时刻刻会萦绕脑海,而想搞清楚这些,就不得不提到Android
自身的 输入系统 (Input System
)。Android
自身的 输入系统 是什么?任何与Android
设备的交互——称之为 输入事件,都需要通过 输入系统 进行管理和分发;这其中最靠近上层,并且最典型的一个小环节就是View
的 事件分发 流程。
4.2 系统进程输入系统
Android
系统在启动的时候,会初始化zygote
进程和由zygote
进程fork
出来的SystemServer
进程;作为 系统进程 之一,SystemServer
进程会提供一系列的系统服务,而InputManagerService
也正是由SystemServer
提供的。- 在
SystemServer
的初始化过程中,InputManagerService
(下称IMS
)和WindowManagerService
(下称WMS
)被创建出来;其中WMS
本身的创建依赖IMS
对象的注入:// SystemServer.java private void startOtherServices() { InputManagerService inputManager = new InputManagerService(context); // inputManager作为WindowManagerService的构造参数 WindowManagerService wm = WindowManagerService.main(context,inputManager, ...); }
- 在 输入系统 中,
WMS
非常重要,其负责管理IMS
、Window
与ActivityManager
之间的通信,先来看IMS
。 IMS
服务的作用就是负责输入模块在Java
层级的初始化,并通过JNI
调用,在Native
层进行更下层输入子系统相关功能的创建和预处理。- 在
JNI
的调用过程中,IMS
创建了NativeInputManager
实例,NativeInputManager
则在初始化流程中又创建了EventHub
和InputManager
:NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { // ... // 创建一个EventHub对象 sp<EventHub> eventHub = new EventHub(); // 创建一个InputManager对象 mInputManager = new InputManager(eventHub, this, this); }
- 对于整个
Native
层级而言,其向下负责与Linux
的设备节点中获取输入,向上则与靠近用户的Java
层级相通信,可以说是非常重要。而在该层级中,EventHub
和InputManager
又是最核心的两个角色。 - 首先来说
EventHub
,它是底层 输入子系统 中的核心类,负责从物理输入设备中不断读取事件(Event
),然后交给InputManager
,后者内部封装了InputReader
和InputDispatcher
,用来从EventHub
中读取事件和分发事件InputManager::InputManager(...) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize(); }
EventHub
建立了Linux
与输入设备之间的通信,InputManager
中的InputReader
和InputDispatcher
负责了输入事件的读取和分发,在 输入系统 中,两者的确非常重要。
4.3 组件类型ANR
ANR
是一套监控Android
应用响应是否及时的机制,可以把发生ANR
比作是 引爆炸弹,那么整个流程包含三部分组成:- 埋定时炸弹:中控系统(
system_server
进程)启动倒计时,在规定时间内如果目标(应用进程)没有干完所有的活,则中控系统会定向炸毁(杀进程)目标。 - 拆炸弹:在规定的时间内干完工地的所有活,并及时向中控系统报告完成,请求解除定时炸弹,则幸免于难。
- 引爆炸弹:中控系统立即封装现场,抓取快照,搜集目标执行慢的罪证(
traces
),便于后续的案件侦破(调试分析),最后是炸毁目标。
- 埋定时炸弹:中控系统(
4.4 Input类型ANR
Input
类型的ANR
在日常开发中更为常见且更复杂,比如用户或者测试反馈,点击屏幕中的UI元素导致「卡死」。- 少数情况下开发者能够很快定位到问题,但更常见的情况是,该问题是 随机 且 难以复现 的,导致该问题的原因也更具有综合性,比如低端设备的系统本身资源已非常紧张,或者多线程相互持有彼此需要的资源导致 死锁 ,亦或其它复杂的情况,因此处理这类型问题就需要开发者对 输入系统 中的
ANR
机制有一定的了解。 - 和组件类
ANR
不同的是,Input
类型的超时机制并非时间到了一定就会爆炸,而是处理后续上报事件的过程才会去检测是否该爆炸,所以更像是 扫雷 的过程。 - 扫雷,对于 输入系统 而言,即使某次事件执行时间超过预期的时长,只要用户后续没有再生成输入事件,那么也不需要
ANR
。 - 而只有当新一轮的输入事件到来,此时正在分发事件的窗口(即
App
应用本身)迟迟无法释放资源给新的事件去分发,这时InputDispatcher
才会根据超时时间,动态的判断是否需要向对应的窗口提示ANR
信息。 - 这也正是用户在第一次点击屏幕,即使事件处理超时,也没有弹出
ANR
窗口,而当用户下意识再次点击屏幕时,屏幕上才提示出了ANR
信息的原因。 - 由此可见,组件类
ANR
和Input ANR
原理上确实有所不同;除此之外,前者是在ActivityManager
线程中处理的ANR
信息,后者则是在InputDispatcher
线程中处理的ANR
4.5 事件分发异步机制
- 先抛出一个新的问题,对处于
system_server
进程Native
层级的 事件分发 而言,其向下与 应用进程 的通信的过程应该是同步还是异步的?- 对于读者而言,不难得出答案是异步的,因为两者之间双向通信的建立是通过
SocketPair
,并且,因为system_server
中InputDispatcher
对事件的分发实际上是一对多的,如果是同步的,那么一旦其中一个应用分发超时,那么InputDispatcher
线程自然被卡住,其永远都不可能进入到下一轮的事件分发中,扫雷 机制更是无从谈起。 - 因此,与应用进程中事件分发不同的是,后者我们通常可以认为是在主线程中同步的,而对于整个 输入系统 而言,因为涉及到 系统进程 与多个 应用进程 之间异步的通信,因此其内部的实现更为复杂。
- 因为事件分发涉及到异步回调机制,因此
InputDispatcher
需要对事件进行维护和管理,那么问题就变成了,使用什么样的数据结构去维护这些输入事件比较合适。
- 对于读者而言,不难得出答案是异步的,因为两者之间双向通信的建立是通过
- 三个队列
InputDispatcher
的源码实现中,整体的事件分发流程共使用到3个事件队列:
- mInBoundQueue:用于记录
InputReader
发送过来的输入事件; - outBoundQueue:用于记录即将分发给目标应用窗口的输入事件;
- waitQueue:用于记录已分发给目标应用,且应用尚未处理完成的输入事件。
- 笔者通过2轮事件分发的示例,对三个队列的作用进行简单的梳理。
- 第一轮事件分发
- 首先
InputReader
线程通过EventHub
监听到底层的输入事件上报,并将其放入了mInBoundQueue
中,同时唤醒了InputDispatcher
线程。 - 然后
InputDispatcher
开始了第一轮的事件分发,此时并没有正在处理的事件,因此InputDispatcher
从mInBoundQueue
队列头部取出事件,并重置ANR
的计时,并检查窗口是否就绪,此时窗口准备就绪,将该事件转移到了outBoundQueue
队列中,因为应用管道对端连接正常,因此事件从outBoundQueue
取出,然后放入了waitQueue
队列,因为Socket
双向通信已经建立,接下来就是 应用进程 接收到新的事件,然后对其进行分发。 - 如果 应用进程 事件分发正常,那么会通过
Socket
向system_server
通知完成,则对应的事件最终会从waitQueue
队列中移除。
- 首先
- 第二轮事件分发
- 如果第一轮事件分发尚未接收到回调通知,第二轮事件分发抵达又是如何处理的呢?
- 第二轮事件到达
InputDispatcher
时,此时InputDispatcher
发现有事件正在处理,因此不会从mInBoundQueue
取出新的事件,而是直接检查窗口是否就绪,若未就绪,则进入ANR
检测状态。
- 以下几种情况会导致进入
ANR
检测状态:1、目标应用不会空,而目标窗口为空。说明应用程序在启动过程中出现了问题; 2、目标
Activity
的状态是Pause
,即不再是Focused
的应用; 3、目标窗口还在处理上一个事件。 - 读者需要理解,并非所有「目标窗口还在处理上一个事件」都会抛出
ANR
,而是需要通过检测时间,如果未超时,那么直接中止本轮事件分发,反之,如果事件分发超时,那么才会确定ANR
的发生。- 这也正是将
Input
类型的ANR
描述为 扫雷 的原因:这里的扫雷是指当前输入系统中正在处理着某个耗时事件的前提下,后续的每一次input
事件都会检测前一个正在处理的事件是否超时(进入扫雷状态),检测当前的时间距离上次输入事件分发时间点是否超时。如果前一个输入事件,则会重置ANR
的timeout
,从而不会爆炸。 - 至此,输入系统 检测到了
ANR
的发生,并向上层抛出了本次ANR
的相关信息。
- 这也正是将
05.ANR监控排查思路
5.1 ANR监控整体思路
- Android系统监测ANR的核心原理是消息调度和超时处理。
5.2 目前流行方案
- 1、使用FileObserver监听 /data/anr/traces.txt的变化
- 缺点:高版本ROM需要root权限。解决方案:海外Google Play服务、国内Hardcoder
- 2、监控消息队列的运行时间(WatchDog)
- 利用主线程的消息队列处理机制,应用发生卡顿,一定是在dispatchMessage中执行了耗时操作。通过给主线程的Looper设置一个Printer,打点统计dispatchMessage方法执行的时间,如果超出阀值,表示发生卡顿,则dump出各种信息,提供开发者分析性能瓶颈。
- 为卡顿监控代码增加ANR的线程监控,在发送消息时,在ANR线程中保存一个状态,主线程消息执行完后再Reset标志位。如果在ANR线程中收到发送消息后,超过一定时间没有复位,就可以任务发生了ANR。
5.3 如何查看ANR日志
- 如何查看ANR信息?
- 1、抓取bugreport。adb shell bugreport > bugreport.txt
- 2、直接导出/data/anr/traces.txt文件。adb pull /data/anr/traces.txt
5.4 导出ANR信息
- 在AS的Terminal中,使用: adb pull data/anr/traces.txt 要存储在本地的路径
- 导出上面提到的ANR现场信息文件;导出来后,便可对文件内容进行详细分析:从CPU、IO、锁冲突等原因思考;
- 导出ANR生成文件遇到的问题
- 输入:adb pull data/anr/traces.txt /Users/didi/yc 报错
- 报错日志:adb: error: failed to stat remote object 'data/anr/traces.txt': No such file or directory
- 为何会出现这种情况
- 查了些资料,发现厂商有对这块做优化。以前anr一直放在traces文件中,多次出现有覆盖的问题。高版本厂商做了优化,会根据时间戳分别生成一个文件,打包导出。
- 遇到权限问题
- 在查看data文件夹时,看到权限不允许。ls: .: Permission denied,这个该怎么办呢?
- 使用 adb bugreport 导出文件
- 此命令会导出一个zip压缩包,解压后在FS/data/anr目录下就可以看到traces文件了。adb bugreport 命令也可以指定文件导出目录
5.5 ANR具体如何分析
- ANR问题是由于主线程的任务在规定时间内没处理完任务,而造成这种情况的原因大致会有一下几点:
- 主线程在做一些耗时的工作导致线程卡死
- 主线程被其他线程锁
- cpu被其他进程占用,该进程没被分配到足够的cpu资源。
- 然后看anr日志。在发生ANR的时候,系统会收集ANR相关的信息提供给开发者:首先在Log中有ANR相关的信息,其次会收集ANR时的CPU使用情况,还会收集trace信息,也就是当时各个线程的执行情况。trace文件保存到了/data/anr/traces.txt中
- 从log中找到ANR反生的信息:会包含了ANR的时间、进程、是何种ANR等信息。
- 在该条log之后会有CPU usage的信息,表明了CPU在ANR前后的用量(log会表明截取ANR的时间),从各种CPU Usage信息中大概可以分析如下几点:
- 如果某些进程的CPU占用百分比较高,几乎占用了所有CPU资源,而发生ANR的进程CPU占用为0%或非常低,则认为CPU资源被占用,进程没有被分配足够的资源,从而发生了ANR。这种情况多数可以认为是系统状态的问题,并不是由本应用造成的。
- 如果发生ANR的进程CPU占用较高,如到了80%或90%以上,则可以怀疑应用内一些代码不合理消耗掉了CPU资源,如出现了死循环或者后台有许多线程执行任务等等原因,这就要结合trace和ANR前后的log进一步分析了。
- 如果CPU总用量不高,该进程和其他进程的占用过高,这有一定概率是由于某些主线程的操作就是耗时过长,或者是由于主进程被锁造成的。
- 除了上述分析CPU usage之后,确定问题需要我们进一步分析trace文件。trace文件记录了发生ANR前后该进程的各个线程的stack。对我们分析ANR问题最有价值的就是其中主线程的stack,一般主线程的trace可能有如下几种情况:
- 主线程是running或者native而对应的栈对应了我们应用中的函数,则很有可能就是执行该函数时候发生了超时。
- 主线程被block:非常明显的线程被锁,这时候可以看是被哪个线程锁了,可以考虑优化代码。如果是死锁问题,就更需要及时解决了。
- 由于抓trace的时刻很有可能耗时操作已经执行完了(ANR -> 耗时操作执行完毕 ->系统抓trace),这时候的trace就没有什么用了,主线程的stack就是这样的:
image
- 总结,就是两个问题
- 1.CPU 问题
- 在 Monkeylog.log 文件中定位到 "anr in" 位置,查看 cpu usage ,total 占用,如发现接近100%,暂时判断为 cpu 问题。
- 然后在 logcat.log 文件中定位到 "not responding" 发生时间,并截取cpuinfo.log 中时间点前后 5s 的 log,然后计算 CPU 占中,看哪个进程用的多,在酌情分析模块的 CPU 占中。
- 2.GC 问题
- 定位到 logcat.log 文件中 "not responding" 发生时间点;
- 去查看发生 ANR 时间点对应的 trace 文件,定位到应用包名,若Dalvik Thread主线程显示“SUSPENDED”,则为内存问题;
- 截取 ANR 发生时间点前 5s 的 log,分析 "dalvikvm" 打印的 Paused GC 耗时,如果过多则定位为 GC 问题,需要查看这 5s 件发生了哪些耗时的操作。
- 1.CPU 问题
5.6 避免ANR操作方案
- 将所有耗时操作,比如访问网络,Socket通信,查询大量SQL 语句,复杂逻辑计算等都放在子线程中去
- 然后通过handler.sendMessage、runOnUIThread、AsyncTask 等方式更新UI。无论如何都要确保用户界面作的流畅度。如果耗时操作需要让用户等待,那么可以在界面上显示度条。
- 使用AsyncTask处理耗时IO操作。
- 在一些同步的操作主线程有可能被锁,需要等待其他线程释放相应锁才能继续执行,这样会有一定的ANR风险,对于这种情况有时也可以用异步线程来执行相应的逻辑。另外, 要避免死锁的发生。
- 使用Thread或者HandlerThread时
- 调用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)设置优先级,否则仍然会降低程序响应,因为默认Thread的优先级和主线程相同。
- 使用Handler处理工作线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。
- 四大组件ANR避免
- Activity的onCreate和onResume回调中尽量避免耗时的代码
- BroadcastReceiver中onReceive代码也要尽量减少耗时,建议使用IntentService处理。
- 各个组件的生命周期函数都不应该有太耗时的操作,即使对于后台Service或者ContentProvider来讲
- 应用在后台运行时候其onCreate()时候不会有用户输入引起事件无响应ANR,但其执行时间过长也会引起Service的ANR和ContentProvider的ANR
05.参考资料
- 需要列出方案设计过程的文档,包括但不局限于PM需求文档,技术参考文档等。
- 微信ANR监测方案
- https://mp.weixin.qq.com/s/fWoXprt2TFL1tTapt7esYg
- 理解Android ANR的触发原理
- http://gityuan.com/2016/07/02/android-anr/
- 微信Android客户端的ANR监控方案
- https://mp.weixin.qq.com/s/fWoXprt2TFL1tTapt7esYg
- 今日头条 ANR 优化实践系列
- https://juejin.cn/post/6947986170135445535
- https://juejin.cn/post/6942665216781975582
- 今日头条 ANR 优化实践系列
- https://mp.weixin.qq.com/s/_Z6GdGRVWq-_JXf5Fs6fsw
- Android SIGQUIT(3) 信号拦截与处理
- https://mp.weixin.qq.com/s?__biz=Mzg4MjE5OTI4Mw==&mid=2247494779&idx=1&sn=b8c84e8e8389543cfbda93cbadd1593c&chksm=cf58f3e7f82f7af1ffff70e1c9f2d6a6a0d0a4b5e4f3f3dd42e9d79fd950bfa32913942ec6d3&scene=178&cur_album_id=2495132597375975425#rd
- 今日头条 ANR 优化实践系列 - 监控工具与分析思路
- https://juejin.cn/post/6942665216781975582
- 稳定性优化:ANR监控方案
- https://juejin.cn/post/7371779128477302823