前言
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);
}
}