ANR类型
类型 | 超时时刻 | 是否有弹窗提示 |
---|---|---|
Activity Timeout | 10s | 有提示 |
BroadcastReceiver Timeout | 10s, 60s | 无感知场景不会提示,如在后台 |
Service Timeout | 20s, 200s | 无感知场景不会提示 |
ContentProvider Timeout | 10s | 无感知场景不会提示 |
InputDispatching Timeout | 5s | 有提示 |
之前的文章,咱们分析了Input ANR
是怎么产生的,详细能够参阅:ANR怎么产生之InputDispatching Timeout
今日这篇文章,咱们来讲讲BroadcastReceiver
和 Service Timeout
ANR分别是怎样产生的。
系统检测ANR的核心原理如下:
- 在流程开始时,发动一个计时器,比方往
MessageQueue
加入一个守时履行的Message
- 假如流程在规则的时刻内完毕,则封闭计时器,比方移除这个
Message
- 假如流程未准时完毕,履行该
Message
,触发ANR
Service Timeout
1. 发动Service时,设置计时器
Service
发动的流程,省掉zygote fork
进程的流程,最终会在system_server
进程中调用到ActiveServices
中的realStartServiceLocked
办法。
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
// 发送delay音讯(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
try {
// 跨进程调用,调用Service所在进程的ActivityThread中的办法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
}
}
在这个办法里,首要做了两件工作:
- 发送
SERVICE_TIMEOUT_MSG
,delay
时刻为SERVICE_TIME_OUT
- 跨进程调用,调用
Service
所在进程的ActivityThread
中的Service初始化办法
bumpServiceExcutingLocked
便是往MesageQueue
里放一个守时履行的Message
。
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
// 当超时后仍没有remove该SERVICE_TIMEOUT_MSG音讯,则履行service Timeout流程
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
这个守时履行的Message
的履行时刻是,当时时刻加上SERVICE_TIME_OUT
。
SERVICE_TIME_OUT
有两种状况:
- 假如是前台
Service
:SERVICE_TIMEOUT = 20s
- 假如是后台
Service
:SERVICE_BACKGROUND_TIMEOUT = 200s
2. Service履行完,移除计时器
在Service
所在进程的ActivityThread
中,履行Service
的初始化办法。
private void handleCreateService(CreateServiceData data) {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
Service service = (Service) cl.loadClass(data.info.name).newInstance();
try {
//创建ContextImpl目标
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//创建Application目标
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//调用服务onCreate()办法
service.onCreate();
// 告诉system_server,service发动完成
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
...
}
}
从上面的办法能够看到,当Service
在进程中初始化完成后,会经过跨进程调用,告诉system_server
。
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
// 移除SERVICE_TIMEOUT_MSG
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
}
3. 假如未准时履行完,履行Message
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
}
}
}
在AMS
的MainHandler
中,会处理SERVICE_TIMEOUT_MSG
,调用ActiveService
的serviceTimeout
办法。
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
// 拼接anr信息
anrMessage = "executing service " + timeout.shortName;
}
}
if (anrMessage != null) {
//当存在timeout的service,履行appNotResponding
mAm.appNotResponding(proc, null, null, false, anrMessage);
}
}
在这个办法里,首要完成拼接ANR信息,以”executing service
“开头,一起调用AMS
的appNotResponding
处理后续的dump和弹窗逻辑。
appNotResponding
的处理和收集信息流程,能够参阅这篇文章:Android产生ANR后的信息收集过程
Broadcast Receiver Timeout
播送分为有序播送和无序播送。
关于无序播送,遍历一切的播送接收者并发送,不关心接收者是否处理完播送音讯。
有序播送,需要等上一个接收者处理完之后,才干发送给后续的接收者,所以会产生ANR。
1. 发送播送时,设置计时器
发送播送的流程,最终会调用processNextBroadcastLocked
来处理播送。这个办法特别长,咱们只截取其间需要的部分。
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
...
// 处理当时有序播送
do {
r = mOrderedBroadcasts.get(0);
// 获取该播送一切的接收者
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
//当播送处理时刻超时,则强制完毕这条播送
broadcastTimeoutLocked(false);
}
}
// 当播送分发完毕,调用了finishReceiverLocked后,r.receivers为null
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
if (r.resultTo != null) {
//处理播送音讯音讯
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
}
// 移除MSG
cancelBroadcastTimeoutLocked();
}
} while (r == null);
r.receiverTime = SystemClock.uptimeMillis();
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
// 设置播送的超时MSG
setBroadcastTimeoutLocked(timeoutTime);
}
// 处理动态播送
if (nextReceiver instanceof BroadcastFilter) {
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
return;
}
// 省掉处理静态播送的流程
}
}
有两种状况会引发BroadcastReceiver的超时:
- 某个播送的总处理时长 > 2 * receiver总个数 + mTimeoutPeriod
- 某个receiver的处理商场超越mTimeoutPeriod
setBroadcastTimeoutLocked的原理便是往Handler里发送一个BROADCAST_TIMEOUT_MSG。
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
2. 移除计时器
cancelBroadcastTimeoutLocked
的调用时机是,播送完毕后,调用AMS
的finishReceiver
办法,然后调用nextReceiver
,最终调用到processNextBroadcastLocked
办法中。
因为这个时候,r.receivers
为null
,所以会履行cancelBroadcastTimeoutLocked
办法。
总结
Service Timeout ANR
产生原理:
- 发动
Service
时,发送一个延时履行的SERVICE_TIMEOUT_MSG
- 假如
Service
在规则的时刻内发动,则移除SERVICE_TIMEOUT_MSG
- 假如
Service
没有在规则的时刻内发动,则履行SERVICE_TIMEOUT_MSG
留意:以上判断ANR
的逻辑,都在system_server
进程中履行。