ANR的原理分析和简单总结
前言
ANR(Application Not Responding),应用无响应,这个可能每个人都碰到过。
该篇主要简单总结下,ANR的几种常见类型(输入事件、广播接收器、Service、ContentProvider),以及ANR一般如何产生的及如何避免。
最后重点是通过源码 了解这几种类型 是如何产生ANR、超时时间是怎么来的、ANR后如何处理的等。
关于 ANR发生后如何分析和处理 这个不在该篇总结,会另起一篇 尽可能比较详细的说明。
ANR简述
几种ANR类型简单总结
几种ANR简单用表格列出下,下面几种情况都会造成ANR:
ANR类型 | 超时时间 | 报错信息 |
---|---|---|
输入事件(按键、触摸等) | 5s | Input event dispatching timed out |
广播BroadcastReceiver | 前台10s,后台/offload 60s | Receiver during timeout of |
Service服务 | Foreground 10s,普通 20s,后台 200s | Timeout executing service |
ContentProvider | 10s | timeout publishing content providers |
如上述表格所述,列出了4种ANR的 超时时间以及报错信息。
在后面,有这 四种会引起ANR类型 源码分析(该篇重点),能够看到超时时间是如何来的,报错如何产生的,产出ANR后是如何处理的(除ContentProvider外,其他都会调用ProcessRecord.appNotResponding())。
注:ContentProvider算不算ANR类型,个人理解 算是,但与其他几种比有点区别。
ANR的产生
ANR的产生就是超时。
长时间无法响应用户输入 或 无效占用资源,会使用户感觉卡顿的感觉。一般这种情况,会弹出对话框提示用户,可以用来选择关闭应用。
这个超时,由 AMS和WMS检测(后面的源码分析可以看到,官网 上也有相关说明:AMS和WMS监控应用的响应性),未在规定时间完成特定任务(如5s未响应输入时间、10s内广播接收器未处理完毕),即会引起ANR。
这个超时,一般由Handler机制 的 延迟发送消息完成。若超时 则发出消息 产生ANR 告知系统及用户,若在时间内完成,则取消消息队列中的延迟消息。
为什么会导致超时,或者导致ANR?
UI线程(主线程) 阻塞:
如在主线程存取较多数据(I/O阻塞),网络阻塞等。也可能是资源 不足造成,如CPU负荷、内存或存储不足等导致I/O阻塞。
死锁和等待:
多线程的死锁和主线程的等待。
ANR的避免
有上面的可能可能原因,可以针对原因进行一些避免。
避免主线程阻塞:
UI线程尽量只做跟UI相关的工作,使用子线程(工作线程) 处理耗时操作或阻塞任务(如数据库、网络、I/O操作);
尽量用Handler来处理UI线程和其他线程之间的交互;
各大组件生命周期中也应避免耗时操作,如Activity的onCreate()、BroadcastReciever的onRecieve()等。广播如有耗时操作,建议放到IntentService中执行。
注: IntentService继承了Service,运行在后台线程中,处理异步需求,工作执行完毕后自动销毁。
但Android版本8.0开始限制后台,API level 30被弃用。8.0及更高版本 建议使用WorkManager 或 JobIntentService替代。
避免CPU负荷或I/O阻塞:
文件或数据操作放到子线程,异步方式完成。
ANR发生后的信息收集
一般ANR发生后 系统会调用appNotResponding()收集信息。
ANR发生后,CPU的负载情况、IOWait、traces文件(/data/anr/traces.txt)都会保存下来,导出log和traces文件进行进一步分析。
不同soc的 log会有不同,注意获取完整的log和traces文件。(如MTK平台,会生成aee_exp文件夹,其所有log也会保存在mtklog文件夹,Q版本之后mtklog变为了debuglogger)
至于 发生ANR错误 如何进行分析并解决,该篇不赘述,计划另起一篇比较详细的总结。
ANR源码分析
下面源码不复杂,主要简单了解下 ANR是如何产生 的。从这个过程,能够看到超时时间是如何来的,报错如何产生的,产出ANR后是如何处理的。
源码也是基于Android10的。
输入事件
在 Android10_原理机制系列_事件传递机制 中,个人感觉 对Android的事件传递过程总结的应该还比较清晰。不过事件传递在Native层过程 很复杂,考虑的状况多,那篇也只看的传递过程的主线。
记得在事件传递机制这篇中,也mark了个ANR的相关点(resetANRTimeoutsLocked()),当时也没细看。这里简单看下输入事件的ANR过程。直接列出 相关源码:
//InputDispatcher.cpp
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry, ...);
}
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, ...) {
if(...) {
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
} else {
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
}
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, applicationHandle, windowHandle,
entry->eventTime, mInputTargetWaitStartTime, reason);
return INPUT_EVENT_INJECTION_PENDING;
}
}
void InputDispatcher::onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
}
void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
nsecs_t newTimeout = mPolicy->notifyANR(
commandEntry->inputApplicationHandle,
commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
commandEntry->reason);
mLock.lock();
resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
commandEntry->inputChannel);
}
接着直接看到mPolicy->notifyANR(),在 Android10_原理机制系列_事件传递机制 中,mPolicy是什么,如何回到java层的都说的比较详细了,这里直接列出回到java层过程的源码:
//com_android_server_input_InputManagerService.cpp
nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<IBinder>& token, const std::string& reason) {
jlong newTimeout = env->CallLongMethod(mServiceObj,
gServiceClassInfo.notifyANR, tokenObj,
reasonObj);
if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
newTimeout = 0; // abort dispatch
} else {
assert(newTimeout >= 0);
}
return newTimeout;
}
//InputManagerService.java
private long notifyANR(IBinder token, String reason) {
return mWindowManagerCallbacks.notifyANR(
token, reason);
}
回到了IMS中,接着就直接看了。
//InputManagerCallback.java
@Override
public long notifyANR(IBinder token, String reason) {
//输入事件log打印,不同情况打印不一样。不过头部都是Input event dispatching timed out
synchronized (mService.mGlobalLock) {
Slog.i(TAG_WM, "Input event dispatching timed out "
+ ...);
}
...
} else if (windowState != null) {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
long timeout = mService.mAmInternal.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
}
}
return 0; // abort dispatching
}
//ActivityManagerService.java
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal {
@Override
public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
synchronized (ActivityManagerService.this) {
return ActivityManagerService.this.inputDispatchingTimedOut(
pid, aboveSystem, reason);
}
}
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
synchronized (this) {
timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
}
if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
return -1;
}
return timeout;
}
boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem, String reason) {
if (proc != null) {
proc.appNotResponding(activityShortComponentName, aInfo,
parentShortComponentName, parentProcess, aboveSystem, annotation);
}
return true;
}
很明显,上面可以看到ANR超时后,会调用appNotResponding()。
超时时间是多少呢?
timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
//ActivityTaskManagerService.java
// How long we wait until we timeout on key dispatching.
public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
//ProcessRecord.java
public long getInputDispatchingTimeout() {
return mWindowProcessController.getInputDispatchingTimeout();
}
//WindowProcessController.java
public long getInputDispatchingTimeout() {
synchronized (mAtm.mGlobalLock) {
return isInstrumenting() || isUsingWrapper()
? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
}
}
很明显,正常情况下 超时事件为 5s。
BroadcastReceiver
简单的从sendBroadcast()开始看一下:
//ContextImpl.java
@Override
public void sendBroadcast(Intent intent) {
try {
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
}
}
//ActivityManagerService.java
@GuardedBy("this")
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 realCallingUid,
int realCallingPid, int userId) {
return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
false /* allowBackgroundActivityStarts */);
}
@GuardedBy("this")
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 realCallingUid,
int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
intent = new Intent(intent);
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
...
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
final BroadcastRecord oldRecord =
replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
if (oldRecord != null) {
...
} else {
//加入到mOrderedBroadcasts队列中
queue.enqueueOrderedBroadcastLocked(r);
//这里接着往下看
queue.scheduleBroadcastsLocked();
}
}
return ActivityManager.BROADCAST_SUCCESS;
}
从sendBroadcast() 到broadcastIntentLocked() ,broadcastIntentLocked() 方法很长,对不同广播进行了不同处理,接着直接看 queue.scheduleBroadcastsLocked() 是如何处理的。
//BroadcastQueue.java
public void scheduleBroadcastsLocked() {
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
private final class BroadcastHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
+ mQueueName + "]");
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
}
}
}
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
processNextBroadcastLocked(fromMsg, false);
}
}
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
r.receiverTime = SystemClock.uptimeMillis();
if (! mPendingBroadcastTimeoutMessage) {
//当前时间戳+超时时间(mConstants.TIMEOUT)
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Submitting BROADCAST_TIMEOUT_MSG ["
+ mQueueName + "] for " + r + " at " + timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
}
...
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
//延迟发送BROADCAST_TIMEOUT_MSG消息。 回到上面看到,接收处理:broadcastTimeoutLocked(true);
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
final void broadcastTimeoutLocked(boolean fromMsg) {
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
//应该完成的时间
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
//未超时
if (timeoutTime > now) {
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
r.receiverTime = now;
if (!debugging) {
r.anrCount++;
}
ProcessRecord app = null;
String anrMessage = null;
Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
...
if (app != null) {
anrMessage = "Broadcast of " + r.intent.toString();
}
if (!debugging && 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));
}
}
scheduleBroadcastsLocked() 通过handler最终执行了 processNextBroadcast()->processNextBroadcastLocked(),其中设置了超时时间 r.receiverTime + mConstants.TIMEOUT;
。 最终通过 setBroadcastTimeoutLocked()->broadcastTimeoutLocked() 处理判断是否超时,生成ANR,最终也是调用的appNotResponding()。
这个超时(mConstants.TIMEOUT) 时间到底是多少呢?
现在具体看下:
//BroadcastQueue.java
final BroadcastConstants mConstants;
BroadcastQueue(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
...
mConstants = constants;
}
在构造BroadcastQueue时 传入的,这个即AMS调用时 queue.scheduleBroadcastsLocked();
中的queue
。
BroadcastQueue queue = broadcastQueueForIntent(intent);
下面来看下这个方法:
//ActivityManagerService.java
BroadcastQueue broadcastQueueForIntent(Intent intent) {
if (isOnOffloadQueue(intent.getFlags())) {
return mOffloadBroadcastQueue;
}
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", backConstants, true);
mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
"offload", offloadConstants, true);
看下定义,这个超时时间即在foreConstants、backConstants、offloadConstants中,在看定义:
//ActivityManagerService.java
// Broadcast policy parameters
final BroadcastConstants foreConstants = new BroadcastConstants(
Settings.Global.BROADCAST_FG_CONSTANTS);
foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;
final BroadcastConstants backConstants = new BroadcastConstants(
Settings.Global.BROADCAST_BG_CONSTANTS);
backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
final BroadcastConstants offloadConstants = new BroadcastConstants(
Settings.Global.BROADCAST_OFFLOAD_CONSTANTS);
offloadConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
// by default, no "slow" policy in this queue
offloadConstants.SLOW_TIME = Integer.MAX_VALUE;
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
同样,不同类型广播 超时时间也是不一样的。 所以很清晰看到:前台广播超时时间10s,后台广播和offload类型,超时是60s。
Service
在总结四大组件中已经说过,service的启动有两种方式:startService()和bindService() 。这里主要看下startService()的流程,ANR是如何产生的。
//ContextImpl.java
@Override
public ComponentName startService(Intent service) {
return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
return cn;
}
}
//ActivityManagerService.java
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage, int userId)
throws TransactionTooLargeException {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, userId);
return res;
}
}
//ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
callingPackage, userId, false);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
final int userId, boolean allowBackgroundActivityStarts)
throws TransactionTooLargeException {
ServiceLookupResult res =
retrieveServiceLocked(service, null, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false, false);
ServiceRecord r = res.record;
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
return r.name;
}
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
if (!isolated) {
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
}
}
}
return null;
}
很简单清晰,不赘述,这里最终可以看到 走到了realStartServiceLocked()方法,由方法名可以猜测是 真正启动服务的关键,下面继续看,这里如何启动服务的不关注,主要看ANR的产生。
//ActiveServices.java
/**
* Note the name of this method should not be confused with the started services concept.
* The "start" here means bring up the instance in the client, and this method is called
* from bindService() as well.
*/
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
r.setProcess(app);
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
final boolean newService = app.services.add(r);
//真正创建服务前执行的
bumpServiceExecutingLocked(r, execInFg, "create");
...
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
...
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}
从注释也能看到,bindService() 也会调用。这里继续看bumpServiceExecutingLocked():
//ActiveServices.java
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
scheduleServiceTimeoutLocked(r.app);
...
r.executeFg |= fg;
r.executeNesting++;
//r.executingStart记录了创建服务开始的时间戳
r.executingStart = now;
}
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;
//延迟发送,即超时时间
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
//ActivityManagerService.java
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
case SERVICE_FOREGROUND_TIMEOUT_MSG: {
mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
} break;
}
}
}
//ActiveServices.java
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
//当前时间 - timeout时间,即最晚开始时间
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
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;
}
}
if (timeout != null && mAm.mProcessList.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);
//超时 记录的信息,即ANR信息
anrMessage = "executing service " + timeout.shortInstanceName;
} else {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
}
if (anrMessage != null) {
//ANR信息传递到进程 处理。
proc.appNotResponding(null, null, null, null, false, anrMessage);
}
}
仔细看下上面代码及注释 就能理解。最终超时信息传入到进程进行处理。 proc.appNotResponding(null, null, null, null, false, anrMessage);
这个超时时间定义如下,注释也很清晰,不同类型服务 超时时间是不一样的。
//ActiveServices.java
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
ContentProvider
在应用启动时,ContentProvider发布 若超时也会发生ANR。
应用启动后,ActivityThread执行attach()操作,最后会执行attachApplicationLocked() 实现上述ANR判断。
//ActivityManagerService.java
// How long we wait for an attached process to publish its content providers
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
/**
* How long we wait for an provider to be published. Should be longer than
* {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
*/
static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
@GuardedBy("this")
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
//可能ANR
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
...
try {
//移除CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG延迟消息
//这里的thread是从ActivityThread传入的,ApplicationThread对象。
thread.bindApplication(processName, appInfo, providers, ...);
}
}
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processContentProviderPublishTimedOutLocked(app);
}
} break;
}
}
}
@GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
}
CONTENT_PROVIDER_PUBLISH_TIMEOUT是10s,10s后发送消息 Hander进行处理,会有timeout publishing content providers信息。在10s内完成,会移除该消息,即不会触发ANR了。
若发生超时,这里没有调用appNotResponding()(不像前3种),这里会杀掉进程并清理了相关信息。
如何移除,看thread.bindApplication(),该方法在延迟发送消息之后执行,即移除延迟消息。如在10s内执行完成,就是不会触发ANR。
注:这是最简单直接看到的一种,移除该消息的调用地方不只一处。
简单看下这个移除延迟消息过程:
//ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
public final void bindApplication(String processName, ApplicationInfo appInfo,
...) {
sendMessage(H.BIND_APPLICATION, data);
}
}
class H extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
}
}
}
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
try {
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
}
}
@UnsupportedAppUsage
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
try {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
//ActivityManagerService.java
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
}