0. 参考:
ANR分析
深入理解 Android ANR 触发原理以及信息收集过程
1.ANR的触发分类:
ANR分为4类:
- InputDispatchTimeout:输入事件分发超时5s,包括按键和触摸事件。
- BroadcastTimeout:比如前台广播在10s内未执行完成,后台60s
- ServiceTimeout:前台服务在20s内未执行完成,后台服务未在200s内完成。
- ContentProviderTimeout:contenProvider在publish后超时10s
2.ANR的触发机制
2.1基本概念
- ANR 的检测和处理逻辑主要写在 AMS(Activity Manager Service) 进程中
- 当超时事件触发后,AMS 会记录下应用主线程的状态,并调用 ActivityManagerService.appNotResponding() 方法来启动 ANR 处理流程。
- 当应用程序在UI线程阻塞太长时间,就会弹出系统弹框,询问我们继续等待还是关闭应用程序,此时发生了ANR。
3.Service,BroadCast,Provider触发ANR:
3.1 ANR是在发生这些调用的时候,就启用"定时炸弹"。
- 1.APP侧:
在Activity中调用startService后,调用链:ContextImpl.startService()->ContextImpl.startServiceCommon() -
- AMS侧:
ActivityManagerService.startService()
->ActiveServices.startServiceLocked()
->ActiveServices.startServiceInnerLocked()
->ActiveServices.bringUpServiceLocked()
->ActivieServices.realStartServiceLocked()
->ActivieServices.realStartServiceLocked
- AMS侧:
- 2.1 其中realStartServiceLocked主要通过向AMS发送一个Handler的延迟消息告诉AMS这个调用超时了。
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
boolean enqueueOomAdj) throws RemoteException { // 2
...............
// 记录此调用的开始---原因是 "create"
// 从此处开始倒计时--发送handler消息到AMS
bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
................
// 通知APP启动Service,执行 handleCreateService
// 调用到 ApplicationThread.scheduleCreateService
thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.mState.getReportedProcState());
..........
- 2.2 ActivieServices.bumpServiceExecutingLocked---->ActivieServices.scheduleServiceTimeoutLocked
这个延迟时间就是1中说到的超时20s, 对应ActiveService的SERVICE_TIMEOUT变量;
void scheduleServiceTimeoutLocked(ProcessRecord proc) { // 2
...................
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT); // 发送延迟消息给AMS的Handler
}
- 2.3 scheduleCreateService
会通过Binder调用回APP侧,最终会调用到ApplicationThread.scheduleCreateService
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s); // 发送Handler消息
}
// 找到处理Handler消息的地方:handleCreateService最终会调用到这个Service的onCreate方法上。
case CREATE_SERVICE:
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
("serviceCreate: " + String.valueOf(msg.obj)));
}
handleCreateService((CreateServiceData)msg.obj); // 执行Service的回调onCreate
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
- 3 小结
1)最开始调用了startService来触发AMS发送一个延迟为SERVICE_TIMEOUT(20s)的Handler事件,
2)并最终回调到实现了这个Service的onCreate函数。
3)如果超过了20s这个Handler消息还是没被取消,那么就会去触发AMS的ANR机制。
3.2 取消延迟发送的Handler消息, “取消炸弹”
- 在上面描述的正常流程中,会调用到handleCreateService,最终会调用removeMessages取消3.1中的延迟发送的Handler消息
private void handleCreateService(CreateServiceData data) {
.....
service.onCreate(); // 进入service的onCreate回调
....
// Service启动完成,需要通知AMS
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
.....
}
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
throw new IllegalArgumentException("Invalid service token");
}
// 执行到ActivityManagerService的 serviceDoneExecuting方法
mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
}
}
// ActivityManagerService的 serviceDoneExecuting方法
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing, boolean enqueueOomAdj) {
......
// 将Handler消息取消
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
..........
}
3.3 ANR的触发, “炸弹引爆”
- 就是延迟发送的Handler消息处理,
在ActivityManagerService.java可看到处理SERVICE_TIMEOUT_MSG的代码
case SERVICE_TIMEOUT_MSG: {
// 调用到ActiveServices的serviceTimeout方法
mServices.serviceTimeout((ProcessRecord) msg.obj);
} break;
//com.android.server.am.ActiveServices.java
void serviceTimeout(ProcessRecord proc) {
................
// 计算是否超时,
if (anrMessage != null) { // 超时
// 触发超时机制,mAm是AMS,mAnrHelper是AnrHelper
mAm.mAnrHelper.appNotResponding(proc, anrMessage);
}
}
- 调用到AnrHelper的appNotResponding方法,最终是调用到AmrConsumerThread().start方法来处理ANR
void appNotResponding(ProcessRecord anrProcess, String annotation) {
appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
false /* aboveSystem */, annotation);
}
void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
synchronized (mAnrRecords) {
mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
parentShortComponentName, parentProcess, aboveSystem, annotation));
}
startAnrConsumerIfNeeded();
}
private void startAnrConsumerIfNeeded() {
if (mRunning.compareAndSet(false, true)) {
new AnrConsumerThread().start(); // 启动AnrConsumerThread线程处理ANR
}
}
private class AnrConsumerThread extends Thread {
@Override
public void run() {
AnrRecord r;
while ((r = next()) != null) {
......
//这里的r就是AnrRecord
r.appNotResponding(onlyDumpSelf);
......
}
}
}
private static class AnrRecord {
void appNotResponding(boolean onlyDumpSelf) {
//mApp是ProcessRecord,mErrorState是ProcessErrorStateRecord
mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
onlyDumpSelf);
}
}
调用到ProcessErrorStateRecord.appNotResponding来弹出无响应弹出。或者dump操作啥的,就是平时见到的ANR
3.4 ANR做了哪些事情
- 主要就是记录ANR错误的相关信息 + 弹出ANR的弹窗
- 最终会调用到ProcessErrorStateRecord的appNotResponding,下面来研究ProcessErrorStateRecord.java的这个函数
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
String parentShortComponentName, WindowProcessController parentProcess,
boolean aboveSystem, String annotation, boolean onlyDumpSelf) { //
........
// 将ANR的相关信息记录到anr文件中
StringBuilder info = new StringBuilder();
.....
StringBuilder report = new StringBuilder();
....................
// 弹出ANR的Dialog
if (mService.mUiHandler != null) {
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
}
}
}
- 小结:
1)在调用得时候发送了延迟HANDLER消息:SERVICE_TIMEOUT_MSG
2)到了延时时间,还未remove掉此handler消息,那么就会触发SERVICE_TIMEOUT_MSG的处理。
3)最终调用到AnrHelper的appNotResponding来处理ANR
4.Input触发ANR:
4.1 从linux侧获取input事件
- 如inputFlinger 讲解的内容,InputReader线程通过EventHub监听/dev/input读取输入事件,监听到事件则把消息传到InputDispatcher(即InputReader把消息写入mInBoundQueue队列)
- InputDispatcher负责把输入事件分发给 目标应用窗口。
4.2 ANR相关
- InputDispatcher的流程:
4.3 ANR触发 代码流程梳理
- InputDispatcher中的方法调用:
1.有等待获取焦点的应用:当前时间超过Timeout,调用processNoFocusedWindowAnrLocked() 进一步确认
2.存在window:当前时间超过事件响应的超时时间。调用onAnrLocked() 进一步确认。
> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 检查是否有任何连接的等待队列具有太旧的事件。如果我们等待事件被确认的时间超过窗口超时,
// 请引发 ANR。返回我们下次应该醒来的时间。
nsecs_t InputDispatcher::processAnrsLocked() {
const nsecs_t currentTime = now();
nsecs_t nextAnrCheck = LONG_LONG_MAX; // 下一次检查anr的时间
// 检查我们是否正在等待一个聚焦窗口出现。如果等待时间过长就报 ANR
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
// 场景1: 触发noFocusedWindow的anr
processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.reset();
mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
// 请继续等待。我们将在mNoFocusedWindowTimeoutTime到来时放弃该事件。
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
}
}
// 检查是否有任何连接 ANR 到期,mAnrTracker 中保存所有已分发事件(未被确认消费的事件)的超时时间
nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
if (currentTime < nextAnrCheck) { // 最有可能的情况
// 一切正常,在 nextAnrCheck 再检查一次
return nextAnrCheck;
}
// 如果我们到达这里,则连接无响应。
sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
// 停止为此无响应的连接唤醒
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
// 场景2: 触发ANR
onAnrLocked(connection);
return LONG_LONG_MIN;
}
4.4 ANR在InputDispatcher的调用栈
6.排查思路:
- adb shell getevent后,点击屏幕,如果有打印,代表驱动是可以拿到点击事件的。如果没有则需要内核的同事查看。如果有则代表是Android的问题。