JasonWang's Blog

Android ANR处理流程

字数统计: 3.1k阅读时长: 14 min
2017/06/25

ANR即Application Not Responding, 应用无响应:当应用在某一个时间内无法及时响应用户请求时,系统会弹出一个对话框,告知用户应用无法响应。此时,用户可以选择关闭应用。对于Android应用来说,有一个UI线程(主线程)专门负责与用户交互,如果在此线程中进行耗时的操作,比如读取磁盘数据、从网络下载资源等IO操作,通常会导致UI线程阻塞,从而无法及时处理用户的输入请求,发生ANR。

Android是通过AMS跟WMS来监控应用响应状态的,一般有如下两种ANR情况:

  • 应用在给定时间内没有对用户输入(按键或者触屏操作)做出响应,通常是5s;
  • 广播接收者(BroadcastReceiver)没有在10s内处理完成;

对于输入无响应的情况,native监控到输入无响应时,则告知WMS,最后由WMS发送消息告诉AMS,某个应用发送了ANR,最后AMS会弹出Application Not Responding的对话框,请求用户关闭应用;对于广播无响应的情况是由AMS负责处理的,AMS对于每个发送出去给广播接收者的广播都有一个10s的定时,如果广播接收者在10s内尚未处理完,则视为无响应,因此也会弹出Application Not Responding

首先来看下第一种情况。

输入无响应

输入无响应是指用户长时间(5s内)无法响应用户的输入操作,如触摸,按键等,此时AMS会弹出对话框提示用户应用出现ANR(Application Not Responding)。

Android由InputManagerService(IMS)负责处理用户的输入事件,IMS会将输入转交给对应的应用窗口,最后由应用本身做出相应的操作;在native层,IMS有一个对应的InputManager来管理来自Kernel的输入事件(有关Android输入系统,请参考罗升阳Android应用程序键盘消息处理机制分析),InputReader负责读取输入事件,而InputDispatcher则负责将读取的事件分发出去。这里,就来看下InputDispatcher是如何检测应用输入无响应以及将其分发出去的。

以下是输入ANR处理流程图(图片来自http://gityuan.com/images/input/input_reader_seq.jpg):

native的InputManager初始化时启动一个InputDispatcherThread用于输入事件的分发:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
....
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

status_t InputManager::start() {
// 分发线程
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
// 读线程
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);


return OK;
}

InputDispatcherThread启动时,进入一个线程循环:

1
2
3
4
5
6
7
8
9
10
11
12

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

//启动后,进入线程循环
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}


InputDispatcher开始事件处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();

// 指令请求队列为空,则开始分发输入事件
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}

// Run all pending commands if there are any.
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock

// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
// 查看线程消息队列是否有新的消息
mLooper->pollOnce(timeoutMillis);
}

根据输入事件的类型将事件分发出去,按键输入事件类型是TYPE_KEY,触屏输入事件TYPE_MOTION,这里假定应用处理的是一个按键事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
switch (mPendingEvent->type) {
....
// 按键事件
case EventEntry::TYPE_KEY: {
...
//分发按键事件
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
// 触屏事件
case EventEntry::TYPE_MOTION: {
....
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}

}

}


分发键盘事件之前,首先需要找到当前响应输入的焦点窗口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
....
// 找到当前输入焦点窗口
Vector<InputTarget> inputTargets;
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
....
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}

在查找目标焦点窗口之前,会检查是否有应用程序的窗口处于无响应状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
....
// Check whether the window is ready for more input
reason = checkWindowReadyForMoreInputLocked(currentTime,
focusedWindowHandle, entry, "focused");
// 输入无响应
if (!reason.isEmpty()) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
//mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
mFocusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.string());
goto Unresponsive;
}

// Done.
Failed:
Unresponsive:
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatisticsLocked(currentTime, entry,
injectionResult, timeSpentWaitingForApplication);
return injectionResult;
}

如果发现焦点窗口无法对用户事件作出响应,并且超过了给定的时间(Android设定的超时为5s),则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

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;
} else {
// Force poll loop to wake up when timeout is due.
if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
*nextWakeupTime = mInputTargetWaitTimeoutTime;
}
return INPUT_EVENT_INJECTION_PENDING;
}
}


将ANR事件以CommandEntry的形式加入到命令队列中,等到下一次输入处理周期时在处理,处理时会调用函数doNotifyANRLockedInterruptible(见dispatchOnce()):

1
2
3
4
5
6
7
8
9
10
11
12
13
14

void InputDispatcher::onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
...
// 将ANR事件添加到命令队列
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
commandEntry->inputApplicationHandle = applicationHandle;
commandEntry->inputWindowHandle = windowHandle;
commandEntry->reason = reason;
}

通过InputDispatcherPolicyInterface接口通知NativeInputMananger有ANR事件发生:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();

nsecs_t newTimeout = mPolicy->notifyANR(
commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
commandEntry->reason);

mLock.lock();

resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
commandEntry->inputWindowHandle != NULL
? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}

到这里,准备通知InputManagerService.java(IMS)ANR事件了,mServiceObj实际是一个Java类InputManagerService的一个全局引用,通过CallLongMethod来调用IMS中的notifyANR方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23


nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) {

JNIEnv* env = jniEnv();

jobject inputApplicationHandleObj =
getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
jobject inputWindowHandleObj =
getInputWindowHandleObjLocalRef(env, inputWindowHandle);
jstring reasonObj = env->NewStringUTF(reason.string());
//调用IMS的notifyANR方法
jlong newTimeout = env->CallLongMethod(mServiceObj,
gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
reasonObj);

env->DeleteLocalRef(reasonObj);
env->DeleteLocalRef(inputWindowHandleObj);
env->DeleteLocalRef(inputApplicationHandleObj);
return newTimeout;
}

NativeInputManager位于com_android_server_input_InputManagerService.cpp,是InputManagerService.java对应的Native文件中的类,其负责初始化输入系统的native框架

IMS收到ANR事件后,通过一个回调接口WindowManagerCallbacks通知WMS(WindowManagerService), WindowManagerCallbacks接口实际是一个InputMonitor.java对象,在SystemServer进程启动时,将WMS中的InputMonitor设置为IMS的回调:

1
2
3
4
5
6
7
8

private long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle, String reason) {
// mWindowMangerCallbacks是InputMonitor实现的回调接口
return mWindowManagerCallbacks.notifyANR(
inputApplicationHandle, inputWindowHandle, reason);
}

向AMS发起inputDispatchingTimedOut的Binder请求,告知其ANR事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

@Override
public long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle, String reason) {
....
if (appWindowToken != null && appWindowToken.appToken != null) {
// 告知AMS有ANR事件
} else if (windowState != null) {
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
windowState.mSession.mPid, aboveSystem, reason);
if (timeout >= 0) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
return timeout * 1000000L; // nanoseconds
}
} catch (RemoteException ex) {
}
}
return 0; // abort dispatching
}

AMS处理输入响应超时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


@Override
public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {

ProcessRecord proc;
long timeout;
synchronized (this) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
}
timeout = getInputDispatchingTimeoutLocked(proc);
}
if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
return -1;
}

return timeout;
}


发现有应用进程ANR,AMS向主线程发送一个可执行对象,执行ANR操作,最后AppErrors会向当前应用程序的UI线程发送一个ANR的对话框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


public boolean inputDispatchingTimedOut(final ProcessRecord proc,
final ActivityRecord activity, final ActivityRecord parent,
final boolean aboveSystem, String reason) {
....
if (proc != null) {
//
mHandler.post(new Runnable() {
@Override
public void run() {
mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
}
});
}

return true;
}

接下来,再来看下广播处理超时的情况。

广播处理超时

广播的运行机制很像“内容发布者-订阅者”这样的信息传递方式:新闻报纸就是典型的例子,订阅者首先得到新闻通讯社注册成为用户才能接收到报纸;而新闻媒体则负责将报纸发送给各个合法的注册者。与此类似,广播的发送者通过Context中的接口sendBroadcast(Intent)向系统发送广播;而接收者则需要通过Context中提供的接口registerReceiver来监听广播,对于有系统权限限制的广播,需要相应的用户权限才允许接收。

Android系统广播由AMS(ActivityManagerService)负责管理,AMS中有两个广播队列,一个是前台广播(Intent中对应的标志位FLAG_RECEIVER_FOREGROUND),具有在前台运行的优先级;一个是后台广播,不指定前台标志位的广播都默认为后台广播。对于前台广播,接收者如果在10s内为完成,则视为超时;后台广播如果接收后60s内为完成,则超时。此时,AMS会向当前UI线程发送一个ANR的对话框。

AMS中接收到广播后,将广播放入一个队列,根据广播接收者注册机制的不同,广播队列分为并行与串行两种处理方式,并行处理广播不用等待其他广播完成,主要用于处理用Context接口注册的广播接收者;串行方式则必须按照严格广播入队列的先后次序来处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
....
// 从Context中注册的广播接收
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
resultExtras, ordered, sticky, false, userId);

final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
// 放入广播队列,并开始处理广播
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}

// AndroidManifest.xml文件中声明的广播接收者
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,

boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
} else {
....
}
}

return ActivityManager.BROADCAST_SUCCESS;
}


发送消息BROADCAST_INTENT_MSG请求处理广播:

1
2
3
4
5
6
7
8
9
10
11

public void scheduleBroadcastsLocked() {
//当前有广播在处理,返回
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}


Handler接受到消息,开始处理队列中的广播:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

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;
}
}
}


处理下一个广播,简单起见,这里只分析串行分发广播的方式:首先记录下广播分发的时间dispatchTime,接着设置广播超时时间;最后开始处理广播,将其发送给广播接收者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {

do {
r = mOrderedBroadcasts.get(0);
// 取消广播超时设置
cancelBroadcastTimeoutLocked();

} while (r == null);
...
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
// 广播分发时间
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
}
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
//设置广播超时消息
setBroadcastTimeoutLocked(timeoutTime);
}
....
// Is this receiver's application already running?
if (app != null && app.thread != null) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
} catch (RuntimeException e) {
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
r.state = BroadcastRecord.IDLE;
return;
}
}
//广播接收者进程不存在,启动之
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, false))
== null) {
//启动应用失败
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
}

如果广播处理在10s内未完成,则出发TIME_OUT的消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}

if (mOrderedBroadcasts.size() == 0) {
return;
}

long now = SystemClock.uptimeMillis();
BroadcastRecord r = mOrderedBroadcasts.get(0);

if (fromMsg) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (timeoutTime > now) {
// 处理成功,重新设置TIMEOUT时间,返回
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
....
r.receiverTime = now;
r.anrCount++;

ProcessRecord app = null;
String anrMessage = null;

Object curReceiver = r.receivers.get(r.nextReceiver-1);
r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
if (curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter)curReceiver;
if (bf.receiverList.pid != 0
&& bf.receiverList.pid != ActivityManagerService.MY_PID) {
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(
bf.receiverList.pid);
}
}
} else {
app = r.curApp;
}

// Move on to the next receiver.
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
// 向超时应用发送ANR消息
if (anrMessage != null) {
mHandler.post(new AppNotResponding(app, anrMessage));
}
}


原文作者:Jason Wang

更新日期:2022-12-13, 19:36:30

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 输入无响应
  2. 2. 广播处理超时