针对个人备忘系列,无意传播,谨慎阅读
文章
- Android异常分析(转)
- ANR问题一般解决思路
- ANR机制以及问题分析
- 应用与系统稳定性第一篇---ANR问题分析的一般套路
- 看完这篇 Android ANR 分析,就可以和面试官装逼了!
- 干货:ANR日志分析全面解析(内含经典堆栈举例)
- 内存泄漏导致的ANR
1. ANR的定义
ANR: Application Not Responding . 应用程序无响应.
- ANR属于应用程序的范畴,ANR明确将问题圈定在应用程序. 而SNR(System Not Responding)反应的是系统进程(system_server)失去了响应能力.
- ANR由消息处理机制保证,Android系统层发现ANR的核心是 消息调度+超时处理 .
- 所有可能导致ANR的调用方式,都会经过系统进程(system_server)调度,然后派发到相应应用进程.一旦APP处理太慢,超时机制就起作用了.它收集一些系统状态,如 CPU/IO的使用情况,进程函数调用栈,弹出ANR对话框.
- ANR本质是要求主线程对任务处理必须及时,不能长时间阻塞.
- 部分ANR问题很难分析,由于系统底层问题导致消息调度失败,出现问题的场景又难以复现.
2. ANR机制
1. ANR机制分为2部分: ANR的监测, ANR的报告
1. ANR的监测.
- 对于不同类型(Broadcast, Service, InputEvent)的ANR,都有对应的监测机制.
2. ANR的报告
- 在监测到ANR时,需要显示ANR对话框,输出日志(发生ANR时候的进程函数调用栈,CPU使用情况等)
3. 整个ANR机制的代码横跨了Android的几个层.
- APP层: APP主线程的处理逻辑
- Framework层: ANR机制的核心
- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
- frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
- frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
- frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
- frameworks/base/core/java/android/view/InputChannel
- frameworks/base/services/core/java/com/android/internal/os/ProcessCpuTracker
- Native层: 输入事件的派发机制.针对InputEvent类型的ANR.
3. ANR的监测机制
1. Service的处理超时
- Service运行在APP的主线程,如果'Service的执行时间()'超过20S,就触发ANR.
- Service中发生ANR,首先检查其生命周期函数是否耗时,无问题则继续检查系统状态,如CPU使用情况,系统服务状态等,判断发生ANR的进程当时是否受系统运行异常的影响.
- Service超时是通过设置定时消息进行检测的.而定时消息是由 ActivityManagerService 实例处理的.
- 具体代码
https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActiveServices.java
//当Service的生命周期开始时,会执行 ActiveServices 的 bumpServiceExecutingLocked,
//进而执行其 scheduleServiceTimeoutLocked
//前台进程中执行Service的超时时间
static final int SERVICE_TIMEOUT = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
//后台进程中执行Service的超时时间
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
public ActiveServices(ActivityManagerService service) {
mAm = service;
***
}
private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why, @Nullable String oomAdjReason) {
***
scheduleServiceTimeoutLocked(r.app);
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
return;
}
//mAm是一个ActivityManagerService实例
//ActivityManagerService实例中的消息队列处理了该定时消息
Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
//如果是前台进程中执行Service,则超时时间是
mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
//当Service的生命周期方法结束,会执行 ActiveServices 的 serviceDoneExecutingLocked .
//在方法中会移除之前添加到 ActivityManagerService 的消息队列中的 SERVICE_TIMEOUT_MSG 定时消息.
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,boolean finishing, boolean enqueueOomAdj) {
***
//移除之前添加到 ActivityManagerService 消息队列中的 SERVICE_TIMEOUT_MSG 定时消息.
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
}
void serviceTimeout(ProcessRecord proc) {
***
String anrMessage = null;
***
if (anrMessage != null) {
//最终会调用 ActivityManagerService 中的 AnrHelper的appNotResponding方法
mAm.mAnrHelper.appNotResponding(proc, anrMessage);
}
}
https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActivityManagerService.java
//如果在超时时间内, SERVICE_TIMEOUT_MSG 定时消息未被清除,则 ActivityManagerService 会执行如下逻辑.
//mServices是ActiveServices实例
final ActiveServices mServices;
final AnrHelper mAnrHelper = new AnrHelper(this);
public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
***
mServices = new ActiveServices(this);
}
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
//若超时时间内Servie未执行完,会调用 ActiveServices 实例的 serviceTimeout 方法
mServices.serviceTimeout((ProcessRecord) msg.obj);
}
break;
}
**
}
- Service超时机制监测总结
- 在Service生命周期开始,ActiveServices 执行 bumpServiceExecutingLocked -> scheduleServiceTimeoutLocked , 将 SERVICE_TIMEOUT_MSG 标记的定时消息添加到 ActivityManagerService 消息队列中.
- 在Service生命周期结束,ActiveServices 执行 serviceDoneExecutingLocked , 将之前添加到 ActivityManagerService 消息队列中的 SERVICE_TIMEOUT_MSG 定时消息移除.
- 若Service的生命周期在定时时间内未执行完成,则 ActivityManagerService 会执行 SERVICE_TIMEOUT_MSG 对应的定时消息, 进而调用 ActiveServices 实例的 serviceTimeout .
- ActiveServices 的 serviceTimeout 最终会调用 ActivityManagerService 实例中 AnrHelper的 appNotResponding方法.
2. Broadcast的处理超时
- App通过在BroadcastReceiver.onReceive()方法中完成对广播的处理.通常onReceive是在主线程中执行. App可以通过执行 registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler)将任务在指定线程对应的Handler中执行.
- 广播消息的调度
https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ActivityManagerService.java
//AMS维护了2个广播队列
//之所有要两个,是为了区分不同的广播超时时间
//前台广播队列超时时间是10S
//后台广播队列超时时间是60S
final BroadcastQueue mFgBroadcastQueue;
final BroadcastQueue mBgBroadcastQueue;
public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,"background", backConstants, true);
***
}
https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/BroadcastQueue.java
//当要派发广播是,ActivityManagerService首先会调用 BroadcastQueue 的 scheduleBroadcastsLocked
//scheduleBroadcastsLocked 会向Handler实例中发送消息 BROADCAST_INTENT_MSG
public void scheduleBroadcastsLocked() {
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
**
}
final BroadcastHandler mHandler;
private final class BroadcastHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
//Handler实例收到 BROADCAST_INTENT_MSG 消息
//执行 processNextBroadcast 方法
processNextBroadcast(true);
}
break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
}
break;
}
}
}
private void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
//processNextBroadcast 会调用 processNextBroadcastLocked 方法
processNextBroadcastLocked(fromMsg, false);
}
}
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
***
//这个方法逻辑非常复杂,直接写出ANR相关的
//ANR 1
broadcastTimeoutLocked(false);
***
//ANR 2
setBroadcastTimeoutLocked(timeoutTime);
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
//ANR 2 : 发送消息到Handler BROADCAST_TIMEOUT_MSG
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true; }
}
}
//ANR2 和 ANR1 最终都走到 broadcastTimeoutLocked 方法
final void broadcastTimeoutLocked(boolean fromMsg) {
***
//和Service的超时监听一样,最终也是调用 ActivityManagerService 中的 AnrHelper的appNotResponding方法
mService.mAnrHelper.appNotResponding(app, anrMessage);
}
final void cancelBroadcastTimeoutLocked() {
if(mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}
}
- 广播消息超时监听逻辑非常复杂,但最终和Service超时监听一样, 会调用 ActivityManagerService 中的 AnrHelper的appNotResponding方法 .
- AMS维护着广播队列BroadcastQueue,AMS线程不断从队列中取出消息进行调度,完成广播消息的派发。 在派发“串行广播消息”时,会抛出一个定时消息BROADCAST_TIMEOUT_MSG,在广播接收器处理完毕后,AMS会将定时消息清除。 如果BROADCAST_TIMEOUT_MSG得到了响应,就会判断是否广播消息处理超时,最终通知ANR的发生。
3. Input处理超时
- 应用程序可以接收输入事件(按键,触屏,轨迹球)等.当5S内没有处理完毕,则触发ANR.
- 输入事件由硬件发起,由InputDispatcher分发到各个需要接受事件的窗口.
- 窗口如何告知 InputDispatcher 自己需要处理输入事件
- Android 通过 InputChannel 连接InputDispatcher和窗口.
- 每个窗口都有一个独立的 InputChannel , 窗口将这个 InputChannel 注册到 InputDispatcher中.
- InputDispatcher
- InputDispatcher 中,窗口每次注册,都会产生1个Connection实例,每个Connection实例维护着2个队列
- outboundQueue : 等待发送给窗口的事件
- waitQueue : 已经发送给窗口的事件
- 输入事件如按键事件是Focused类型,触摸事件则为Touched类型, 对应的处理这些输入事件的窗口,也包括 Focused类型窗口 和 Touched类型窗口.
- InputDispatcher将1个输入事件派发到对应的窗口中
- 首先将事件从关联Connection的outboundQueue中移除
- 然后将事件移动到waitQueue,表示事件已执行完'发送'
- 最后收到窗口'finished'通知,再将该事件从waitQueue移除,表示输入事件已被窗口接收.
- InputDispatcher作为中枢,不断传递输入事件,当1个输入事件无法得到处理,InputDispatcher会放弃该事件,继续处理后续事件,并通知对应APP产生了ANR.
- InputDispatcher 中,窗口每次注册,都会产生1个Connection实例,每个Connection实例维护着2个队列
- 什么时候检测到已产生了ANR: 在派发事件时,需要找到对应类型的窗口,找窗口的过程中会判断对应窗口是否已发生ANR.
- InputDispatcher 中的 findFocusedWindowTargetsLocked 及 findTouchedWindowTargetsLocked 分别用于寻找 Focused 和 Touched 类型窗口.
- 找到窗口后,会判断窗口当前是否有能力处理事件,若发现窗口之前已经等待超过了超时限制,就触发ANR.
- 触发ANR.会调用Java层的 InputManagerService.notifyANR
- Java层 InputManagerService.notifyANR
- InputManagerService.notifyANR 最终会调用 ActivityManagerService.inputDispatchingTimedOut .
boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName, *** if (proc != null) { *** //最终会调用 ActivityManagerService 中的 AnrHelper的appNotResponding方法 mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo, parentShortComponentName, parentProcess, aboveSystem, annotation); } return true; } - 最终会调用 ActivityManagerService 中的 AnrHelper的appNotResponding方法
- InputManagerService.notifyANR 最终会调用 ActivityManagerService.inputDispatchingTimedOut .
4. ANR超时监测机制总结
- Service ANR. 前台进程中的Service生命周期函数执行不能超过20S,后台进程不能超过200S.
- Broadcast ANR. 前台的串行广播消息必须在10S内处理完毕,后台的串行广播消息必须在60S内处理完毕.
- Input ANR. 输入事件必须在5S内处理完毕.
- Service和Broadcast的ANR,超时监测都是在Java层实现,由ActivityManagerService中Handler的TIMEOUT消息进行检查; 而Input ANR的超时监测,则是在Native层,由InputDispatcher实现.
- ANR监测到后,最终都调用 ActivityManagerService 中 AnrHelper实例的 appNotResponding 方法.
4. ANR的报告机制
1. appNotResponding 方法源码
class AnrHelper {
void appNotResponding(***) {
startAnrConsumerIfNeeded();
}
private void startAnrConsumerIfNeeded() {
if (mRunning.compareAndSet(false, true)) {
new AnrConsumerThread().start();
}
}
private class AnrConsumerThread extends Thread {
private AnrRecord next() {
synchronized (mAnrRecords) {
return mAnrRecords.isEmpty() ? null : mAnrRecords.remove(0);
}
}
@Override
public void run() {
AnrRecord r;
***
r.appNotResponding(onlyDumpSelf);
}
}
private static class AnrRecord {
final ProcessRecord mApp;
void appNotResponding(boolean onlyDumpSelf) {
mApp.appNotResponding(mActivityShortComponentName, mAppInfo,
mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
onlyDumpSelf);
}
}
}
class ProcessRecord implements WindowProcessListener {
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
String parentShortComponentName, WindowProcessController parentProcess,
boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
//1: 从最近运行进程(Last Recent Used)中挑选,填充 firstPids和lastPids
//firstPids用于保存ANR进程及其父进程, system_server进程 和 persistent的进程(譬如Phone进程)
//lastPids用于保存除firstPids外的其他进程
ArrayList<Integer> firstPids = new ArrayList<>(5);
SparseArray<Boolean> lastPids = new SparseArray<>(20);
//2: 更新CPU使用信息. ANR的第一次CPU信息采样.
mService.updateCpuStatsNow();
//3: 在 event log 中打印ANR信息
EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
annotation);
//将ANR进程ID保存在firstPisd中
firstPids.add(pid);
parentPid = parentProcess.getPid();
//将ANR进程的父进程ID保存在firstPids中
firstPids.add(parentPid);
for (int i = getLruProcessList().size() - 1; i >= 0; i--) {
ProcessRecord r = getLruProcessList().get(i);
if (r != null && r.thread != null) {
int myPid = r.pid;
if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
if (r.isPersistent()) {
//将persistent的进程ID保存在firstPids中
firstPids.add(myPid);
} else if (r.treatLikeActivity) {
firstPids.add(myPid);
} else {
//将其他进程的ID保存在lastPids中
lastPids.put(myPid, Boolean.TRUE);
}
}
}
}
//4: 在 main log 中打印ANR信息
StringBuilder info = new StringBuilder();
info.append("ANR in ").append(processName);
**
//5: 打印调用栈
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
nativePids, tracesFileException, offsets);
//6: 更新CPU使用信息. ANR第二次对CPU使用信息进行采样
mService.updateCpuStatsNow();
//7: 打印 anr 类型的 dropBox异常信息
mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
parentShortComponentName, parentPr, annotation, report.toString(), tracesFile,
null);
//8: 显示ANR对话框
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
mService.mUiHandler.sendMessage(msg);
}
}
1. appNotResponding 方法总结
appNotResponding 中主要逻辑:
- 第1次更新CPU使用信息.
- 填充 firstPids 和 lastPids 数组,发生ANR的进程被填在firstPids第一位,这样打印函数栈时候,当前ANR进程总是在 trace 文件最前面
- 打印函数调用栈,具体由 ActivityManagerService.dumpStackTraces 实现.
- 第2次更新CPU使用信息.
- 第1次CPU信息是ANR发生前的CPU使用情况
- 第2次CPU信息是ANR发生后的CPU使用情况
- 显示ANR对话框
- 除了上述主题逻辑,发生ANR时还会打印各类型日志:
- event log,通过检索”am_anr”关键字,可以找到发生ANR的应用
- main log,通过检索”ANR in “关键字,可以找到ANR的信息,日志的上下文会包含CPU的使用情况
- dropbox,通过检索”anr”类型,可以找到ANR的信息
- traces, 发生ANR时,各进程的函数调用栈信息