一直以来只知道Android上ANR是因为主线程卡住了,一般5秒就会出现ANR的dialog,一直也没有去深究这个来源。最近在写一个测试程序的时候发现,在主线程上调用了一个阻塞的binder 调用,阻塞了很长时间居然没有ANR,很奇怪,所以终于下决心好好研究下这个问题。
要搞清楚这个问题,所掌握的线索就只有ANR的时候所弹出的dialog,那么就从dialog上的字符串开始,还好这个“not responding”还是比较独特的。很容易就找到了AppNotResponding这个dilaog类,他是BaseErrorDialog类的子类,果然不出所料他是一个WindowManager.LayoutParams.TYPE_SYSTEM_ALERT的类,而且设置了很多的flag,保证它能显示在APP的前面,不去深究这个了,还是看看是谁让它显示的呢?
早ActivityManagerService里找到一段代码:
case SHOW_NOT_RESPONDING_MSG: {
synchronized (ActivityManagerService.this) {
HashMap data = (HashMap) msg.obj;
ProcessRecord proc = (ProcessRecord)data.get("app");
if (proc != null && proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
return;
}
Intent intent = new Intent("android.intent.action.ANR");
if (!mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
}
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
if (mShowDialogs) {
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
mContext, proc, (ActivityRecord)data.get("activity"),
msg.arg1 != 0);
d.show();
proc.anrDialog = d;
} else {
// Just kill the app if there is no dialog to be shown.
killAppAtUsersRequest(proc, null);
}
}
ensureBootCompleted();
} break;
可以发现有人给ActivityManagerService发了这个
SHOW_NOT_RESPONDING_MSG
的消息,才导致了这个可恶的dialog给show了出来,是谁,站出来,我保证不打死它。
在同一个文件里发现了
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
......
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
HashMap map = new HashMap();
msg.what = <span style="color: rgb(255, 102, 102);">SHOW_NOT_RESPONDING_MSG</span>;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}
mHandler.sendMessage(msg);
}
}
再继续往前追溯真凶 ,原来是InputMonitor搞得鬼。而InputMonitor是WindowManager放到InputManager中的回调。它有一个notifyANR的方法才是这个事件的源头。
// Native callback.
private long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle) {
return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, inputWindowHandle);
}
这是InputManagerService里面的代码,Native callback,看到这个注释会崩溃,有木有。
等一等,ANR和InputManagerService 扯上了,什么情况。看来不追到native的代码是不行了。
void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
nsecs_t newTimeout = mPolicy-><span style="color:#ff6666;">notifyANR</span>(
commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
commandEntry->reason);
mLock.lock();
resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
commandEntry->inputWindowHandle != NULL
? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}
Native的InputDispatcher会报告这个情况。 在InputDispatcher::handleTargetsNotReadyLocked方法中有这么段代码:
if (<span style="color:#ff6666;">currentTime >= mInputTargetWaitTimeoutTime</span>) {
onANRLocked(currentTime, applicationHandle, windowHandle,
......
}
而
mInputTargetWaitTimeoutTime = currentTime + timeout;
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
DEFAULT_INPUT_DISPATCHING_TIMEOUT 就是5秒。ok就是这个了。
再进一步查看是谁调用这个doNotifyANRLockedInterruptible, 最终可以发现是在:
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
这个loop中,这样就可以理解了,原来是在分发input事件 的时候超时了,才会出现这个一连串的反映,最终导致ANR 的dialog给show了出来。
再度感叹,好复杂。不过借此倒可以学习到InputManager - WindowManger -ActivityManager 三者的一定的概念。