ANR
ANR
ANR(Application Not Responding),应用程序无响应。ANR 属于应用程序的范畴,ANR 由消息处理机制保证,Android 在系统层实现了一套精密的机制来发现 ANR,核心原理是消息调度
和超时处理
。
其次,ANR 机制主体实现在系统层。所有与 ANR 相关的消息,都会经过系统进程(system_server)调度,然后派发到应用进程完成对消息的实际处理,同时,系统进程设计了不同的超时限制来跟踪消息的处理。一旦应用程序处理消息不当,超时限制就起作用了,它收集一些系统状态,例如:CPU/IO使用情况、进程函数调用栈,并且报告用户有进程无响应了(ANR 对话框)。
然后,ANR 问题本质是一个性能问题。ANR 机制实际上对应用程序主线程的限制,要求主线程在限定的时间内处理完一些最常见的操作(启动服务、处理广播、处理输入),如果处理超时,则认为主线程已经失去了响应其他操作的能力。主线程中的耗时操作,例如:密集CPU运算、大量IO、复杂界面布局等,都会降低应用程序的响应能力。
ANR 机制
ANR 机制可以分为两部分:
ANR 的监测:Android 对于不同的 ANR 类型(Broadcast,Service,InputEvent)都有一套监测机制。
ANR 的报告:在监测到 ANR 以后,需要显示 ANR 对话框、输出日志(发生 ANR 时的进程函数调用栈、CPU 使用情况等)。
ANR 的触发原因
谷歌文档中对 ANR 产生的原因是这么描述的:
Android 系统中的应用被 ActivityManagerService 及 WindowManagerService 两个系统服务监控着,系统会在如下两种情况展示出 ANR 的对话框!
KeyDispatchTimeout ( 5 seconds ) :按键或触摸事件在特定时间内无响应
BroadcastTimeout ( 10 seconds ):BroadcastReceiver 在特定时间内无法处理完成
ServiceTimeout ( 20 seconds ) :Service 在特定的时间内无法处理完成
Service 超时监测机制
Service 运行在应用程序的主线程,如果 Service 的执行时间超过 20 秒,则会引发 ANR。
当发生 Service ANR 时,一般可以先排查一下在 Service 的生命周期函数中有没有做耗时的操作,例如复杂的运算、IO 操作等。如果应用程序的代码逻辑查不出问题,就需要深入检查当前系统的状态:CPU 的使用情况、系统服务的状态等,判断当时发生 ANR 进程是否受到系统运行异常的影响。
系统是如何检测 Service 超时的呢?Android 是通过设置定时消息实现的。定时消息是由 AMS 的消息队列处理的,AMS 有 Service 运行的上下文信息,所以在 AMS 中设置一套超时检测机制也是合情合理的。
Service ANR 机制相对最为简单,主体实现在ActiveServices中。
在 Service 的启动流程中,Service 进程 attach 到 system_server 进程后会调用 realStartServiceLocked() 方法。
// frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
public final class ActiveServices {
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
// 发送 delay 消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
boolean created = false;
try {
// 最终执行服务的 onCreate() 方法
app.thread.scheduleCreateService(r, r.serviceInfo, mAm.
compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
... ...
}
}
}
复制代码
private final void bumpServiceExecutingLocked(...) {
scheduleServiceTimeoutLocked(r.app);
}
复制代码
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
// 当超时后仍没有 remove 该 SERVICE_TIMEOUT_MSG 消息,
// 通过 AMS.MainHandler 抛出一个定时消息。
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
复制代码
AMS.MainHandler 抛出一个定时消息 SERVICE_TIMEOUT_MSG。
前台进程中执行 Service,超时时间是 SERVICE_TIMEOUT(20 秒)
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
复制代码
后台进程中执行 Service,超时时间是 SERVICE_BACKGROUND_TIMEOUT(200 秒)
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
复制代码
当 Service 的生命周期结束时(不会 ANR),会调用 serviceDoneExecutingLocked() 方法,之前抛出的 SERVICE_TIMEOUT_MSG 消息在这个方法中会被清除。
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
... ...
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}
}
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
... ...
if (r.executeNesting <= 0) {
if (r.app != null) {
... ...
// 当前服务所在进程中没有正在执行的service,清除 SERVICE_TIMEOUT_MSG 消息
if (r.app.executingServices.size() == 0) {
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
... ...
}
... ...
}
复制代码
如果没有 Remove 掉 SERVICE_TIMEOUT_MSG,在 system_server 进程中有一个 Handler 线程,名叫 ActivityManager。
如果在超时时间内,SERVICE_TIMEOUT_MSG 没有被清除,便会向该 Handler 线程发送一条信息 SERVICE_TIMEOUT_MSG。
final class MainHandler extends Handler {
... ...
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
... ...
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
... ...
}
}
复制代码
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
... ...
long nextTime = 0;
// 寻找运行超时的 Service
for (int i = proc.executingServices.size() - 1; i >= 0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
if (sr.executingStart < maxTime) {
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
// 判断执行 Service 超时的进程是否在最近运行进程列表,如果不在,则忽略这个 ANR
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer,
LAST_ANR_LIFETIME_DURATION_MSECS);
anrMessage = "executing service " + timeout.shortName;
... ...
}
if (anrMessage != null) {
// 当存在 timeout 的 service,则执行 appNotResponding
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
复制代码
上述方法会找到当前进程已经超时的 Service,经过一些判定后,决定要报告 ANR,最终调用 AMS.appNotResponding() 方法。
走到这一步,ANR 机制已经完成了监测报告任务,剩下的任务就是 ANR 结果的输出,我们称之为 ANR 的报告机制。ANR 的报告机制是通过 AMS.appNotResponding() 完成的,Broadcast 和 InputEvent 类型的 ANR 最终也都会调用这个方法。
Broadcast 超时处理
应用程序可以注册广播接收器,实现 BroadcastReceiver.onReceive() 方法来完成对广播的处理。通常,这个方法是在主线程执行的,Android 限定它执行时间不能超过10秒,否则,就会引发 ANR。
onReceive() 也可以调度在其他线程执行,通过 Context.registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) 这个方法注册广播接收器,可以指定一个处理的 Handler,将 onReceive() 调度在非主线程执行。
广播消息的调度
AMS 维护了两个广播队列 BroadcastQueue:
foreground queue,前台队列的超时时间是 10 秒。
background queue,后台队列的超时时间是 60 秒。
之所以有两个,就是因为要区分不同超时时间。所有发送的广播都会进入到队列中等待调度,在发送广播时,可以通过 Intent.FLAG_RECEIVER_FOREGROUND 参数将广播投递到前台队列。
AMS 会不断地从队列中取出广播消息派发到各个 BroadcastReceiver。当要派发广播时,AMS 会调用 BroadcastQueue.scheduleBroadcastsLocked() 方法!
public final class BroadcastQueue {
public void scheduleBroadcastsLocked() {
if (mBroadcastsScheduled) { return; }
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
}
复制代码
上述方法中,往 AMS 的消息队列发送 BROADCAST_INTENT_MSG 消息,由此也可以看到真正派发广播的是 AMS 线程。由于上述方法可能被并发调用,所以通过 mBroadcastsScheduled 这个变量来标识 BROADCAST_INTENT_MSG 是不是已经被 AMS 接收了,当已经抛出的消息还未被接受时,不需要重新抛出。
public final class BroadcastQueue {
private final class BroadcastHandler extends Handler {
public BroadcastHandler(Looper looper) {
super(looper, null, true);
}
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
} } break;
} } } }
复制代码
接下来调用 BroadcastQueue.processNextBroadcast() 方法,参数为 true 表示这是一次来自 BROADCAST_INTENT_MSG 消息的派发请求。
阶段 1:处理并行广播信息
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
... ...
//阶段一:处理非串行广播消息
① 设置mBroadcastsScheduled
if (fromMsg) {
// fromMsg 参数为 true 表示这是一次来自 BROADCAST_INTENT_MSG 消息的派发请求
mBroadcastsScheduled = false;
}
② 处理“并行广播消息”
while (mParallelBroadcasts.size() > 0) {
... ...
final int N = r.receivers.size();
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
... ...
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
... ...
}
// ③ 处理阻塞的广播消息
if (mPendingBroadcast != null) {
... ...
if (!isDead) {
// It's still alive, so keep waiting // isDead表示当前广播消息的进程的存活状态 // 如果还活着,则返回该函数,继续等待下次派发 return; } ... ... } 复制代码
1、设置mBroadcastsScheduled
该变量在前文说过,是对 BROADCAST_INTENT_MSG 进行控制。 如果是响应 BROADCAST_INTENT_MSG 的派发调用,则将 mBroadcastsScheduled 设为false, 表示本次 BROADCAST_INTENT_MSG 已经处理完毕,可以继续抛出下一次 BROADCAST_INTENT_MSG 消息了。
2、处理“并行广播消息”
我们知道广播接收器有“动态”和“静态”之分,通过 Context.registerReceiver() 注册的广播接收器为“动态”的,通过 AndroidManifest.xml 注册的广播接收器为“静态”的。
广播消息有“并行”和“串行”之分,“并行广播消息”都会派发到“动态”接收器,“串行广播消息”则会根据实际情况派发到两种接收器。
在BroadcastQueue维护着两个队列:
mParallelBroadcasts:“并行广播消息”都会进入到此队列中排队。“并行广播消息”可以一次性派发完毕,即在一个循环中将广播派发到所有的“动态”接收器。
mOrderedBroadcasts:“串行广播消息”都会进入到此队列中排队。“串行广播消息”需要轮侯派发,当一个接收器处理完毕后,会再抛出 BROADCAST_INTENT_MSG 消息,再次进入 BroadcastQueue.processNextBroadcast() 处理下一个。
3、处理阻塞的广播消息
有时候会存在一个广播消息派发不出去的情况,这个广播消息会保存在 mPendingBroadcast 变量中。新一轮的派发启动时,会判断接收该消息的进程是否还活着,如果接收进程还活着,那么就继续等待。否则,就放弃这个广播消息。
阶段 2:处理串行广播信息
接下来是最为复杂的一部分,处理“串行广播消息”,ANR 监测机制只在这一类广播消息中才发挥作用,也就是说“并行广播消息”是不会发生ANR的。
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
... ...
阶段一:处理并行广播消息
阶段二:处理串行广播消息
boolean looped = false;
do {
... ...
r = mOrderedBroadcasts.get(0);
boolean forceReceive = false;
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
// ① 广播消息的第一个ANR监测机制
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) && (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers)) && !mService.mAnrManager.isAnrDeferrable()) {
broadcastTimeoutLocked(false);
// forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
... ...
// ② 判断该广播消息是否处理完毕
if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) {
... ...
cancelBroadcastTimeoutLocked();
... ...
mOrderedBroadcasts.remove(0);
r = null;
looped = true;
continue;
}
} while (r == null);
}
复制代码
这部分是一个 do-while 循环,每次都从 mOrderedBroadcasts 队列中取出第一条广播消息进行处理。第一个 Broadcast ANR 监测机制千呼万唤总算是出现了:
1、判定当前时间是否已经超过了 r.dispatchTime + 2×mTimeoutPeriod×numReceivers。
dispatchTime 表示这一系列广播消息开始派发的时间。“串行广播消息”是逐个接收器派发的,一个接收器处理完毕后,才开始处理下一个消息派发。开始派发到第一个接收器的时间就是 dispatchTime。 dispatchTime 需要开始等广播消息派发以后才会设定,也就是说,第一次进入processNextBroadcast()时,dispatchTime=0,并不会进入该条件判断。
mTimeoutPeriod 由当前 BroadcastQueue 的类型决定(forground为10秒,background为60秒)。这个时间在初始化 BroadcastQueue 的时候就设置好了,本意是限定每一个 Receiver 处理广播的时间,这里利用它做了一个超时计算。
2、如果广播消息已经处理完毕,则从mOrderedBroadcasts中移除,重新循环,处理下一条;否则,就会跳出循环。
阶段 3:设定定时消息
以上代码块完成的主要任务是从队列中取一条“串行广播消息”,接下来就准备派发了。
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
... ...
阶段一:处理并行广播消息
阶段二:处理串行广播消息
阶段三:设定定时消息
... ...
// 串行广播消息的第二个ANR监测机制
r.receiverTime = SystemClock.uptimeMillis();
... ...
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
}
复制代码
取出“串行广播消息”后,一旦要开始派发,第二个ANR检测机制就出现了。
mPendingBroadcastTimeoutMessage 变量用于标识当前是否有阻塞的超时消息,如果没有则调用 BroadcastQueue.setBroadcastTimeoutLocked():
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
mService.mAnrManager.sendBroadcastMonitorMessage(timeoutTime, mTimeoutPeriod);
}
}
复制代码
通过设置一个定时消息 BROADCAST_TIMEOUT_MSG 来跟踪当前广播消息的执行情况,这种超时监测机制跟 Service ANR 很类似,也是抛到 AMS 线程的消息队列。如果所有的接收器都处理完毕了,则会调用 cancelBroadcastTimeoutLocked() 清除该消息;否则,该消息就会响应,并调用 broadcastTimeoutLocked(),这个方法在第一种 ANR 监测机制的时候调用过,第二种 ANR 监测机制也会调用。
阶段 4:向“动态”接收器派发广播消息
设置完定时消息后,就开始派发广播消息了,首先是“动态”接收器:
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
... ...
阶段一:处理并行广播消息
阶段二:处理串行广播消息
阶段三:设定定时消息
阶段四:向“动态”接收器派发广播消息
final BroadcastOptions brOptions = r.options;
final Object nextReceiver = r.receivers.get(recIdx);
// 动态接收器的类型都是BroadcastFilter
if (nextReceiver instanceof BroadcastFilter) {
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
... ...
return;
}
复制代码
“动态”接收器的载体进程一般是处于运行状态的,所以向这种类型的接收器派发消息相对简单,调用 BroadcastQueue.deliverToRegisteredReceiverLocked() 完成接下来的工作。
阶段 5:向“静态”接收器派发广播消息
“静态”接收器是在AndroidManifest.xml中注册的,派发的时候,可能广播接收器的载体进程还没有启动,所以,这种场景会复杂很多。
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
... ...
阶段一:处理并行广播消息
阶段二:处理串行广播消息
阶段三:设定定时消息
阶段四:向“动态”接收器派发广播消息 阶段五:向“静态”接收器派发广播消息
// 静态接收器的类型都是 ResolveInfo
ResolveInfo info = (ResolveInfo)nextReceiver;
// ① 权限检查
ComponentName component = new ComponentName(info.activityInfo.applicationInfo.packageName, info.activityInfo.name);
... ...
String targetProcess = info.activityInfo.processName;
// ② 获取接收器所在的进程
ProcessRecord app = mService.getProcessRecordLocked(targetProcess, info.activityInfo.applicationInfo.uid, false);
... ...
// ③ 进程已经启动
// Is this receiver's application already running? if (app != null && app.thread != null && !app.killed) { try { app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats); processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { ... ... } } // ④ 进程还未启动 if ((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == null) { logBroadcastReceiverDiscardLocked(r); finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); scheduleBroadcastsLocked(); r.state = BroadcastRecord.IDLE; return; } // ⑤ 进程启动失败 mPendingBroadcast = r; mPendingBroadcastRecvIndex = recIdx; } } 复制代码
1、权限检查
“静态”接收器是 ResolveInfo ,需要通过 PackageManager 获取包信息,进行权限检查。
2、获取接收器所在的进程
经过一系列复杂的权限检查后,终于可以向目标接收器派发了。通过 AMS.getProcessRecordLocked() 获取广播接收器的进程信息。
3、进程已经启动
如果 app.thread != null ,则进程已经启动,就可以调用 BroadcastQueue.processCurBroadcastLocked() 进行接下来的派发处理了。
4、进程还未启动
如果进程还没有启动,则需要通过 AMS.startProcessLocked() 来启动进程,当前消息并未派发,调用 BroadcastQueue.scheduleBroadcastsLocked() 进入下一次的调度。
5、进程启动失败
如果进程启动失败了,则当前消息记录成 mPendingBroadcast ,即阻塞的广播消息,等待下一次调度时处理。
最终有两个方法将广播消息派发出去: BroadcastQueue.deliverToRegisteredReceiverLocked() 和 BroadcastQueue.processCurBroadcastLocked()。
抛开这两个函数的逻辑,试想要将广播消息从 AMS 所在的 system_server 进程传递到应用程序的进程,该怎么实现?
自然需要用到跨进程调用,Android中最常规的手段就是 Binder机制
对于应用程序已经启动(app.thread != null)的情况,会通过 IApplicationThread 发起跨进程调用:
// Is this receiver's application already running? if (app != null && app.thread != null && !app.killed) { try { app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats); processCurBroadcastLocked(r, app); 复制代码
private final void processCurBroadcastLocked(BroadcastRecord r, ProcessRecord app) throws RemoteException {
... ...
boolean started = false;
try {
mService.notifyPackageUse(r.intent.getComponent().getPackageName(), PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
// 通过 IApplicationThread 发起跨进程调用
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId, app.repProcState);
started = true;
} finally {
... ...
}
}
复制代码
ActivityThread.ApplicationThread.scheduleReceiver()
└── ActivityThread.handleReceiver()
└── BroadcastReceiver.onReceive()
复制代码
对于应用程序还未启动的情况,会调用 IIntentReceiver 发起跨进程调用,应用进程的实现在LoadedApk.ReceiverDispatcher.IntentReceiver中,调用关系如下:
LoadedApk.ReceiverDispatcher.IntentReceiver.performReceive()
└── LoadedApk.ReceiverDispatcher.performReceiver()
└── LoadedApk.ReceiverDispatcher.Args.run()
└── BroadcastReceiver.onReceive()
复制代码
最终都会调用到 BroadcastReceiver.onReceive() ,在应用进程执行接收广播消息的具体动作。
对于“串行广播消息”而言,执行完了以后,还需要通知 system_server 进程,才能继续将广播消息派发到下一个接收器,这又需要跨进程调用了。
应用进程在处理完广播消息后,即在 BroadcastReceiver.onReceive() 执行完毕后,会调用 BroadcastReceiver.PendingResult.finish() , 接下来的调用关系如下:
BroadcastReceiver.PendingResult.finish()
└── BroadcastReceiver.PendingResult.sendFinished()
└── IActivityManager.finishReceiver()
└── ActivityManagerService.finishReceiver()
└── BroadcastQueue.processNextBroadcat()
复制代码
通过IActivityManager发起了一个从应用进程到 system_server 进程的调用,最终在AMS线程中,又走到了 BroadcastQueue.processNextBroadcat(), 开始下一轮的调度。
两种 ANR 机制最终都会调用 BroadcastQueue.broadcastTimeoutLocked() 方法, 第一种 ANR 监测生效时,会将 fromMsg 设置为 false ;第二种 ANR 监测生效时,会将 fromMsg 参数为 true 时,表示当前正在响应 BROADCAST_TIMEOUT_MSG 消息。
final void broadcastTimeoutLocked(boolean fromMsg) {
// ① 设置mPendingBroadcastTimeoutMessage
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
mService.mAnrManager.removeBroadcastMonitorMessage();
}
... ...
long now = SystemClock.uptimeMillis();
// ② 判断第二种ANR机制是否超时
BroadcastRecord r = mOrderedBroadcasts.get(0);
if (fromMsg) {
... ...
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (timeoutTime > now) {
... ...
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
... ...
// ③ 已经超时,则结束对当前接收器,开始新一轮调度 finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
// ④ 抛出绘制ANR对话框的消息
if (anrMessage != null) {
// Post the ANR to the handler since we do not want to process ANRs while
// potentially holding our lock.
mHandler.post(new AppNotResponding(app, anrMessage));
}
}
复制代码
1、设置mPendingBroadcastTimeoutMessage
mPendingBroadcastTimeoutMessage 标识是否存在未处理的 BROADCAST_TIMEOUT_MSG 消息,将其设置成false,允许继续抛出BROADCAST_TIMEOUT_MSG消息。
2、判断第二种ANR机制是否超时
每次将广播派发到接收器,都会将 r.receiverTime 更新,如果判断当前还未超时,则又抛出一个 BROADCAST_TIMEOUT_MSG 消息。 正常情况下,所有接收器处理完毕后,才会清除 BROADCAST_TIMEOUT_MSG ;否则,每进行一次广播消息的调度,都会抛出 BROADCAST_TIMEOUT_MSG 消息。
3、已经超时,则结束对当前接收器,开始新一轮调度
判断已经超时了,说明当前的广播接收器还未处理完毕,则结束掉当前的接收器,开始新一轮广播调度。
4、抛出绘制ANR对话框的消息
最终,发出绘制 ANR 对话框的消息。
AMS 维护着广播队列 BroadcastQueue ,AMS 线程不断从队列中取出消息进行调度,完成广播消息的派发。在派发“串行广播消息”时,会抛出一个定时消息 BROADCAST_TIMEOUT_MSG ,在广播接收器处理完毕后,AMS 会将定时消息清除。如果 BROADCAST_TIMEOUT_MSG 得到了响应,就会判断是否广播消息处理超时,最终通知ANR的发生。
Input 超时处理
应用程序可以接收输入事件(按键、触屏、轨迹球等),当 5秒 内没有处理完毕时,则会引发 ANR。
输入事件经历了一些什么工序才能被派发到应用的界面?
如何检测输入事件处理超时?
输入事件派发工序
输入事件最开始由硬件设备(如按键或触摸屏幕)发起,Android 有一套输入子系统来发现各种输入事件,这些事件最终都会被 InputDispatcher 分发到各个需要接收事件的窗口。那么,窗口如何告之 InputDispatcher 自己需要处理输入事件呢?Android 通过 InputChannel 连接 InputDispatcher 和窗口,InputChannel 其实是封装后的 Linux管道(Pipe)。每一个窗口都会有一个独立的 InputChannel ,窗口需要将这个 InputChannel 注册到 InputDispatcher 中:
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
...
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
...
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
...
mLooper->wake();
return OK;
}
复制代码
对于 InputDispatcher 而言,每注册一个 InputChannel 都被视为一个 Connection ,通过文件描述符来区别。InputDispatcher 是一个消息处理循环,当有新的 Connection 时,就需要唤醒消息循环队列进行处理。
输入事件的类型有很多,按键、轨迹球、触屏等,Android 对这些事件进行了分类,处理这些事件的窗口也被赋予了一个类型(targetType):Foucused 或 Touched。
如果当前输入事件是按键类型,则寻找 Focused 类型的窗口。如果当前输入事件类型是触摸类型,则寻找 Touched 类型的窗口。
InputDispatcher 需要经过以下复杂的调用关系,才能把一个输入事件派发出去(调用关系以按键事件为例,触屏事件的调用关系类似):
InputDispatcherThread::threadLoop()
└── InputDispatcher::dispatchOnce()
└── InputDispatcher::dispatchOnceInnerLocked()
└── InputDispatcher::dispatchKeyLocked()
└── InputDispatcher::dispatchEventLocked()
└── InputDispatcher::prepareDispatchCycleLocked()
└── InputDispatcher::enqueueDispatchEntriesLocked()
└── InputDispatcher::startDispatchCycleLocked()
└── InputPublisher::publishKeyEvent()
复制代码
具体每个函数的实现逻辑此处不表。我们提炼出几个关键点:
InputDispatcherThread 是一个线程,它处理一次消息的派发。
输入事件作为一个消息,需要排队等待派发,每一个 Connection 都维护两个队列:
· outboundQueue: 等待发送给窗口的事件。每一个新消息到来,都会先进入到此队列。
· waitQueue: 已经发送给窗口的事件。
publishKeyEvent 完成后,表示事件已经派发了,就将事件从 outboundQueue 挪到了 waitQueue。
复制代码
事件经过这么一轮处理,就算是从 InputDispatcher 派发出去了,但事件是不是被窗口收到了,还需要等待接收方的 “finished” 通知。在向 InputDispatcher 注册 InputChannel 的时候,同时会注册一个回调函数 handleReceiveCallback():
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
...
for (;;) {
...
status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
if (status) { break; }
d->finishDispatchCycleLocked(currentTime, connection, seq, handled); ...
}
...
d->unregisterInputChannelLocked(connection->inputChannel, notify);
}
复制代码
当收到的 status 为 OK 时,会调用 finishDispatchCycleLocked() 来完成一个消息的处理:
InputDispatcher::finishDispatchCycleLocked()
└── InputDispatcher::onDispatchCycleFinishedLocked()
└── InputDispatcher::doDispatchCycleFinishedLockedInterruptible()
└── InputDispatcher::startDispatchCycleLocked()
复制代码
调用到 doDispatchCycleFinishedLockedInterruptible() 方法时,会将已经成功派发的消息从 waitQueue 中移除, 进一步调用会 startDispatchCycleLocked 开始派发新的事件。
至此,我们回答了第一个问题:
一个正常的输入事件会经过从 outboundQueue 挪到 waitQueue 的过程,表示消息已经派发出去;再经过从 waitQueue 中移除的过程,表示消息已经被窗口接收。InputDispatcher 作为中枢,不停地在递送着输入事件,当一个事件无法得到处理的时候,InputDispatcher 不能就此死掉啊,否则系统也太容易崩溃了。InputDispatcher 的策略是放弃掉处理不过来的事件,并发出通知(这个通知机制就是ANR),继续进行下一轮消息的处理。
理解输入事件分发模型,我们可以举一个生活中的例子:
每一个输入事件可以比做一个快递,InputDispatcher 就像一个快递中转站,窗口就像是收件人,InputChannel 就像是快递员。 所有快递都会经过中转站中处理,中转站需要知道每一个快递的收件人是谁,通过快递员将快递发送到具体的收件人。 这其中有很多场景导致快递不能及时送到:譬如联系不到收件人;快递很多,快递员会忙不过来;快递员受伤休假了等等… 这时候快递员就需要告知中转站:有快递无法及时送到了。中转站在收到快递员的通知后,一边继续派发其他快递,一边报告上级。
复制代码
检测输入事件处理超时
在派发事件时,dispatchKeyLocked() 和 dispatchMotionLocked(),需要找到当前的焦点窗口,焦点窗口才是最终接收事件的地方,找窗口的过程就会判断是否已经发生了ANR:
InputDispatcher::findFocusedWindowTargetsLocked()
InputDispatcher::findTouchedWindowTargetsLocked()
└── InputDispatcher::handleTargetsNotReadyLocked()
└── InputDispatcher::onANRLocked()
└── InputDispatcher::doNotifyANRLockedInterruptible()
└── NativeInputManager::notifyANR()
复制代码
(1)首先,会调用 findFocusedWindowTargetsLocked() 或 findTouchedWindowTargetsLocked() 寻找接收输入事件的窗口。
在找到窗口以后,会调用 checkWindowReadyForMoreInputLocked() 检查窗口是否有能力再接收新的输入事件,会有一系列的场景阻碍事件的继续派发:
场景1: 窗口处于paused状态,不能处理输入事件
“Waiting because the [targetType] window is paused.”
场景2: 窗口还未向InputDispatcher注册,无法将事件派发到窗口
“Waiting because the [targetType] window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.”
场景3: 窗口和InputDispatcher的连接已经中断,即InputChannel不能正常工作
“Waiting because the [targetType] window’s input connection is [status]. The window may be in the process of being removed.”
场景4: InputChannel已经饱和,不能再处理新的事件
“Waiting because the [targetType] window’s input channel is full. Outbound queue length: %d. Wait queue length: %d.”
场景5: 对于按键类型(KeyEvent)的输入事件,需要等待上一个事件处理完毕
“Waiting to send key event because the [targetType] window has not finished processing all of the input events that were previously delivered to it. Outbound queue length: %d. Wait queue length: %d.”
场景6: 对于触摸类型(TouchEvent)的输入事件,可以立即派发到当前的窗口,因为TouchEvent都是发生在用户当前可见的窗口。但有一种情况, 如果当前应用由于队列有太多的输入事件等待派发,导致发生了ANR,那TouchEvent事件就需要排队等待派发。
“Waiting to send non-key event because the %s window has not finished processing certain input events that were delivered to it over %0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.”
(2)然后,上述有任何一个场景发生了,则输入事件需要继续等待,紧接着就会调用 handleTargetsNotReadyLocked() 来判断是不是已经的等待超时了:
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
...
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime, reason);
*nextWakeupTime = LONG_LONG_MIN;
return INPUT_EVENT_INJECTION_PENDING;
}
...
}
复制代码
最后,如果当前事件派发已经超时,则说明已经检测到了 ANR ,调用 onANRLocked() 方法,然后将 nextWakeupTime 设置为最小值,马上开始下一轮调度。在 onANRLocked() 方法中, 会保存 ANR 的一些状态信息,调用 doNotifyANRLockedInterruptible() ,进一步会调用到 JNI 层的 NativeInputManager::notifyANR() 方法,它的主要功能就是衔接 Native 层和 Java 层,直接调用 Java 层的 InputManagerService.notifyANR() 方法。
至此,ANR的处理逻辑转交到了Java层。底层(Native)发现一旦有输入事件派发超时,就会通知上层(Java),上层收到ANR通知后,决定是否终止当前输入事件的派发。
发生ANR时,Java层最开始的入口是 InputManagerService.notifyANR() ,它是直接被 Native 层调用的。我们先把 ANR 的Java层调用关系列出来:
InputManagerService.notifyANR()
└── InputMonitor.notifyANR()
├── IApplicationToken.keyDispatchingTimedOut()
│ └── ActivityRecord.keyDispatchingTimedOut()
│ └── AMS.inputDispatchingTimedOut()
│ └── AMS.appNotResponding()
└── AMS.inputDispatchingTimedOut()
└── AMS.appNotResponding()
复制代码
InputManagerService.notifyANR() 只是为 Native层 定义了一个接口,它直接调用 InputMonitor.notifyANR()。如果该方法的返回值等于0,则放弃本次输入事件;如果大于0,则表示需要继续等待的时间。
public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) {
...
if (appWindowToken != null && appWindowToken.appToken != null) {
// appToken实际上就是当前的ActivityRecord。
// 如果发生ANR的Activity还存在,则直接通过ActivityRecord通知事件派发超时
boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);
if (! abort) {
return appWindowToken.inputDispatchingTimeoutNanos;
}
} else if (windowState != null) {
// 如果发生ANR的Activity已经销毁了,则通过AMS通知事件派发超时
long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut( windowState.mSession.mPid, aboveSystem, reason);
if (timeout >= 0) { return timeout; }
}
return 0;
// abort dispatching
}
复制代码
上述方法中有两种不同的调用方式,但最终都会交由 AMS.inputDispatchingTimedOut() 处理。AMS 有重载的 inputDispatchingTimedOut() 方法,他们的参数不一样。ActivityRecord 调用时,可以传入的信息更多一点(当前发生ANR的界面是哪一个)。
@Override public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
// 1. 根据进程号获取到ProcessRecord
proc = mPidsSelfLocked.get(pid);
...
// 2. 获取超时时间
// 测试环境下的超时时间是INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT(60秒),
// 正常环境下的超时时间是KEY_DISPATCHING_TIMEOUT(5秒)
timeout = getInputDispatchingTimeoutLocked(proc);
// 调用重载的函数,如果返回True,则表示需要中断当前的事件派发;
if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) { return -1; }
// 3. 返回继续等待的时间,这个值会传递到Native层
return timeout;
}
public boolean inputDispatchingTimedOut(final ProcessRecord proc, final ActivityRecord activity, final ActivityRecord parent, final boolean aboveSystem, String reason) {
...
// 1. 发生ANR进程正处于调试状态,不需要中断事件
if (proc.debugging) { return false; }
// 2. 当前正在做dexopt操作,这会比较耗时,不需要中断
if (mDidDexOpt) {
// Give more time since we were dexopting.
mDidDexOpt = false; return false;
}
// 3. 发生ANR的进程是测试进程,需要中断,但不在UI界面显示ANR信息判断
if (proc.instrumentationClass != null) { ...
finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info); return true;
}
// 4. 通知UI界面显示ANR信息
mHandler.post(new Runnable() {
@Override public void run() {
appNotResponding(proc, activity, parent, aboveSystem, annotation);
} });
...
return true;
}
复制代码
在 InputDispatcher 派发输入事件时,会寻找接收事件的窗口,如果无法正常派发,则可能会导致当前需要派发的事件超时(默认是5秒)。Native层发现超时了,会通知Java层,Java层经过一些处理后,会反馈给Native层,是继续等待还是丢弃当前派发的事件。
ANR 信息收集过程
// frameworks/base/services/core/java/com/android/server/am/AppErrors.java
class AppErrors {
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
... ...
long anrTime = SystemClock.uptimeMillis();
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow(); // 更新 cpu 统计信息
}
boolean showBackground = Settings.Secure.
getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
boolean isSilentANR;
synchronized (mService) {
if (mService.mShuttingDown) {
return;
} else if (app.notResponding) {
return;
} else if (app.crashing) {
return;
} else if (app.killedByAm) {
return;
} else if (app.killed) {
return;
}
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
app.notResponding = true;
// 记录 ANR 到 EventLog
EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
app.processName, app.info.flags, annotation);
// 将当前进程添加到 firstPids
firstPids.add(app.pid);
// Don't dump other PIDs if it's a background ANR
isSilentANR = !showBackground
&& !isInterestingForBackgroundTraces(app);
if (!isSilentANR) {
int parentPid = app.pid;
if (parent != null && parent.app != null && parent.app.pid > 0) {
parentPid = parent.app.pid;
}
if (parentPid != app.pid) firstPids.add(parentPid);
// 将 system_server 进程添加到 firstPids
if (MY_PID != app.pid
&& MY_PID != parentPid) firstPids.add(MY_PID);
for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mService.mLruProcesses.get(i);
if (r != null && r.thread != null) {
int pid = r.pid;
if (pid > 0 && pid != app.pid
&& pid != parentPid && pid != MY_PID) {
if (r.persistent) {
// 将 persistent 进程添加到 firstPids
firstPids.add(pid);
} else if (r.treatLikeActivity) {
firstPids.add(pid);
} else {
// 其他进程添加到 lastPids
lastPids.put(pid, Boolean.TRUE);
}
}
}
}
}
}
// 记录 ANR 输出到 main log
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(app.processName);
if (activity != null && activity.shortComponentName != null) {
info.append(" (").append(activity.shortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(app.pid).append("\n");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("\n");
}
if (parent != null && parent != activity) {
info.append("Parent: ").append(parent.shortComponentName).append("\n");
}
// 创建 CPU tracker 对象
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
... ...
// 输出 traces 信息
File tracesFile = ActivityManagerService.dumpStackTraces(
true, firstPids,
(isSilentANR) ? null : processCpuTracker,
(isSilentANR) ? null : lastPids,
nativePids);
String cpuInfo = null;
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow();
synchronized (mService.mProcessCpuTracker) {
cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
}
// 记录当前 CPU 负载情况
info.append(processCpuTracker.printCurrentLoad());
info.append(cpuInfo);
}
// 记录从 anr 时间开始的 Cpu 使用情况
info.append(processCpuTracker.printCurrentState(anrTime));
// 输出当前 ANR 的 reason,以及 CPU 使用率、负载信息
Slog.e(TAG, info.toString());
if (tracesFile == null) {
Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
}
... ...
// 将 traces 文件和 CPU 使用率信息保存到 dropbox,即 data/system/dropbox 目录
mService.addErrorToDropBox("anr", app, app.processName,
activity, parent, annotation, cpuInfo, tracesFile, null);
... ...
synchronized (mService) {
mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
// 后台 ANR 的情况, 直接杀掉
if (isSilentANR) {
app.kill("bg anr", true);
return;
}
// 设置 app 的 ANR 状态,病查询错误报告 receiver
makeAppNotRespondingLocked(app,
activity != null ? activity.shortComponentName : null,
annotation != null ? "ANR " + annotation : "ANR",
info.toString());
// 弹出 ANR 对话框
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
// 向 ui 线程发送,内容为 SHOW_NOT_RESPONDING_MSG 的消息
mService.mUiHandler.sendMessage(msg);
}
}
}
复制代码
当发生 ANR 时, 会按顺序依次执行:
1、输出 ANR Reason 信息到 EventLog,也就是说 ANR 触发的时间点最接近的就是 EventLog 中输出的 am_anr 信息;
2、收集并输出重要进程列表中的各个线程的 traces 信息,该方法较耗时;
3、输出当前各个进程的 CPU 使用情况以及 CPU 负载情况;
4、将 traces 文件和 CPU 使用情况信息保存到 dropbox,即 data/system/dropbox 目录;
5、根据进程类型,来决定直接后台杀掉,还是弹框告知用户。
ANR输出重要进程的traces信息,这些进程包含:
1、firstPids 队列:第一个是 ANR 进程,第二个是 system_server,剩余是所有 persistent 进程;
2、Native 队列:是指 /system/bin/ 目录的 mediaserver、sdcard 以及 surfaceflinger 进程;
3、lastPids 队列: 是指 mLruProcesses 中的不属于 firstPids 的所有进程。
dump 出 trace 信息的流程:
// ActivityManagerService.java
public static File dumpStackTraces(boolean clearTraces, ... ,nativePids) {
... ...
if (tracesDirProp.isEmpty()) {
// 默认为 data/anr/traces.txt
String globalTracesPath =
SystemProperties.get("dalvik.vm.stack-trace-file", null);
tracesFile = new File(globalTracesPath);
try {
if (clearTraces && tracesFile.exists()) {
tracesFile.delete(); // 删除已存在的 traces 文件
}
// 这里会保证 data/anr/traces.txt 文件内容是全新的方式,而非追加
tracesFile.createNewFile(); // 创建 traces 文件
FileUtils.setPermissions(globalTracesPath, 0666, -1, -1);
} catch (IOException e) {
Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesFile, e);
return null;
}
} else {
}
// 输出 trace 内容
dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids,
extraPids, useTombstonedForJavaTraces);
return tracesFile;
}
复制代码
// ActivityManagerService.java
private static void dumpStackTraces(String tracesFile, ...) {
final DumpStackFileObserver observer;
if (useTombstonedForJavaTraces) {
observer = null;
} else {
observer = new DumpStackFileObserver(tracesFile);
}
// We must complete all stack dumps within 20 seconds.
long remainingTime = 20 * 1000;
try {
if (observer != null) {
observer.startWatching();
}
// 首先,获取 firstPids 进程的 stacks
if (firstPids != null) {
int num = firstPids.size();
for (int i = 0; i < num; i++) {
final long timeTaken;
if (useTombstonedForJavaTraces) {
timeTaken = dumpJavaTracesTombstoned(firstPids.get(i),
tracesFile, remainingTime);
} else {
timeTaken = observer.dumpWithTimeout(firstPids.get(i),
remainingTime);
}
... ...
}
}
// 下一步,获取 native 进程的 stacks
if (nativePids != null) {
for (int pid : nativePids) {
... ...
// 输出 native 进程的 trace
Debug.dumpNativeBacktraceToFileTimeout(
pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
final long timeTaken = SystemClock.elapsedRealtime() - start;
... ...
}
}
// Lastly, dump stacks for all extra PIDs from the CPU tracker.
if (extraPids != null) {
... ...
}
}
} finally {
if (observer != null) {
observer.stopWatching();
}
}
}
复制代码
触发 ANR 时系统会输出关键信息:
1、将 am_anr 信息,输出到 EventLog;
2、获取重要进程 trace 信息,保存到 /data/anr/traces.txt;
3、ANR reason 以及 CPU 使用情况信息,输出到 main log;
4、再将 CPU使用情况 和进程 trace 文件信息,再保存到 /data/system/dropbox。
小结
ANR监测机制包含三种:
Service ANR,前台进程中Service生命周期不能超过 20秒,后台进程中Service的生命周期不能超过 200秒。 在启动Service时,抛出定时消息 SERVICE_TIMEOUT_MSG 或 SERVICE_BACKGOURND_TIMEOUT_MSG,如果定时消息响应了,则说明发生了ANR。
Broadcast ANR,前台的“串行广播消息”必须在 10秒 内处理完毕,后台的“串行广播消息”必须在 60秒 处理完毕, 每派发串行广播消息到一个接收器时,都会抛出一个定时消息 BROADCAST_TIMEOUT_MSG,如果定时消息响应,则判断是否广播消息处理超时,超时就说明发生了ANR。
Input ANR,输入事件必须在 5秒 内处理完毕。在派发一个输入事件时,会判断当前输入事件是否需要等待,如果需要等待,则判断是否等待已经超时,超时就说明发生了ANR。
复制代码
ANR监测机制实际上是对应用程序主线程的要求,要求主线成必须在限定的时间内,完成对几种操作的响应;否则,就可以认为应用程序主线程失去响应能力。
从ANR的三种监测机制中,我们看到不同超时机制的设计:
Service 和 Broadcast 都是由 AMS 调度,利用 Handler 和 Looper,设计了一个 TIMEOUT 消息交由 AMS 线程来处理,整个超时机制的实现都是在Java层;
InputEvent 由 InputDispatcher 调度,待处理的输入事件都会进入队列中等待,设计了一个等待超时的判断,超时机制的实现在Native层。复制代码
转载于//juejin.im/post/5cd04f9b6fb9a03223353da0
还没有评论,来说两句吧...