ANR(Application Not responding),是指應用程序未響應,Android系統對於一些事件須要在必定的時間範圍內完成,若是超過預約時間能未能獲得有效響應或者響應時間過長,都會形成ANR。通常地,這時每每會彈出一個提示框,告知用戶當前xxx未響應,用戶可選擇繼續等待或者Force Close。java
那麼哪些場景會形成ANR呢?android
Service Timeout觸發時機,簡單說就是AMS中的mHandler
收到SERVICE_TIMEOUT_MSG
消息時觸發。git
在前面文章startService流程分析詳細介紹Service啓動流程,在Service所在進程attach到system_server進程的過程當中會調用realStartServiceLocked()
方法數據庫
[-> ActiveServices.java]網絡
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... //發送delay消息(SERVICE_TIMEOUT_MSG),【見小節2.1.2】 bumpServiceExecutingLocked(r, execInFg, "create"); try { ... //最終執行服務的onCreate()方法 app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); } catch (DeadObjectException e) { ... } finally { if (!created) { //當service啓動完畢,則remove SERVICE_TIMEOUT_MSG消息【見小節2.1.3】 serviceDoneExecutingLocked(r, inDestroying, inDestroying); ... } } }
該方法的主要工做發送delay消息(SERVICE_TIMEOUT_MSG)多線程
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) { ... scheduleServiceTimeoutLocked(r.app); } void scheduleServiceTimeoutLocked(ProcessRecord proc) { if (proc.executingServices.size() == 0 || proc.thread == null) { return; } long now = SystemClock.uptimeMillis(); Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj = proc; //當超時後仍沒有remove該SERVICE_TIMEOUT_MSG消息,則執行service Timeout流程【見2.1.4】 mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT)); }
SERVICE_TIMEOUT
,即timeout=20s;SERVICE_BACKGROUND_TIMEOUT
,即timeout=200s;該方法的主要工做是當service啓動完成,則移除service Timeout消息。app
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) { ... if (r.executeNesting <= 0) { if (r.app != null) { r.app.execServicesFg = false; r.app.executingServices.remove(r); if (r.app.executingServices.size() == 0) { //當前服務所在進程中沒有正在執行的service mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app); ... } ... }
到此不難理解,當SERVICE_TIMEOUT_MSG
消息成功發送時,則AMS中的mHandler
收到該消息則觸發調用serviceTimeout
。ide
final class MainHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case SERVICE_TIMEOUT_MSG: { ... //【見小節2.1.5】 mServices.serviceTimeout((ProcessRecord)msg.obj); } break; ... } ... } }
[-> ActiveServices.java]post
void serviceTimeout(ProcessRecord proc) { String anrMessage = null; synchronized(mAm) { if (proc.executingServices.size() == 0 || proc.thread == null) { return; } final long now = SystemClock.uptimeMillis(); 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.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); anrMessage = "executing service " + timeout.shortName; } } if (anrMessage != null) { //當存在timeout的service,則執行appNotResponding【見小節3.1】 mAm.appNotResponding(proc, null, null, false, anrMessage); } }
其中anrMessage的內容爲」executing service [發送超時serviceRecord信息]」;ui
BroadcastQueue Timeout觸發時機,簡單說就是BroadcastQueue中的mHandler
收到BROADCAST_TIMEOUT_MSG
消息時觸發。
在前面文章Android Broadcast廣播機制分析詳細介紹廣播啓動流程,在發送廣播過程當中會執行scheduleBroadcastsLocked
方法來處理相關的廣播,而後會調用到processNextBroadcast
方法來處理下一條廣播。
processNextBroadcast執行過程分4步驟:
[-> BroadcastQueue.java]
final void processNextBroadcast(boolean fromMsg) { synchronized(mService) { ... //step 2: 處理當前有序廣播 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))) { //當廣播處理時間超時,則強制結束這條廣播【見小節2.2.5】 broadcastTimeoutLocked(false); ... } } 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; } //取消BROADCAST_TIMEOUT_MSG消息【見小節2.2.3】 cancelBroadcastTimeoutLocked(); } } while (r == null); ... //step 3: 獲取下條有序廣播 r.receiverTime = SystemClock.uptimeMillis(); if (!mPendingBroadcastTimeoutMessage) { long timeoutTime = r.receiverTime + mTimeoutPeriod; //設置廣播超時時間,發送BROADCAST_TIMEOUT_MSG【見小節2.2.2】 setBroadcastTimeoutLocked(timeoutTime); } ... } }
對於廣播超時處理時機:
final void setBroadcastTimeoutLocked(long timeoutTime) { if (! mPendingBroadcastTimeoutMessage) { Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); mHandler.sendMessageAtTime(msg, timeoutTime); mPendingBroadcastTimeoutMessage = true; } }
設置定時廣播BROADCAST_TIMEOUT_MSG,即當前日後推mTimeoutPeriod時間廣播還沒處理完畢,則進入廣播超時流程。
BROADCAST_FG_TIMEOUT
,即timeout=10s;BROADCAST_BG_TIMEOUT
,即timeout=60s。final void cancelBroadcastTimeoutLocked() { if (mPendingBroadcastTimeoutMessage) { mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this); mPendingBroadcastTimeoutMessage = false; } }
移除廣播超時消息BROADCAST_TIMEOUT_MSG
到此不難理解,當BROADCAST_TIMEOUT_MSG
消息成功發送時,則AMS中的mHandler
收到該消息則觸發調用serviceTimeout
。
private final class BroadcastHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case BROADCAST_TIMEOUT_MSG: { synchronized (mService) { //【見小節2.2.5】 broadcastTimeoutLocked(true); } } break; ... } ... } }
[-> BroadcastRecord.java]
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) { if (mService.mDidDexOpt) { //延遲timeouts直到dexopt結束 mService.mDidDexOpt = false; long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod; setBroadcastTimeoutLocked(timeoutTime); return; } if (!mService.mProcessesReady) { //當系統尚未準備就緒時,廣播處理流程中不存在廣播超時 return; } long timeoutTime = r.receiverTime + mTimeoutPeriod; if (timeoutTime > now) { //過早的timeout,從新設置廣播超時 setBroadcastTimeoutLocked(timeoutTime); return; } } BroadcastRecord br = mOrderedBroadcasts.get(0); if (br.state == BroadcastRecord.WAITING_SERVICES) { //廣播已經處理完成,但須要等待已啓動service執行完成。當等待足夠時間,則處理下一條廣播。 br.curComponent = null; br.state = BroadcastRecord.IDLE; processNextBroadcast(false); return; } r.receiverTime = now; //當前BroadcastRecord的anr次數執行加1操做 r.anrCount++; if (r.nextReceiver <= 0) { return; } ProcessRecord app = null; String anrMessage = null; Object curReceiver = r.receivers.get(r.nextReceiver-1); //根據狀況記錄廣播接收者丟棄的EventLog logBroadcastReceiverDiscardLocked(r); 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; } if (app != null) { anrMessage = "Broadcast of " + r.intent.toString(); } if (mPendingBroadcast == r) { mPendingBroadcast = null; } //繼續移動到下一個廣播接收者 finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); scheduleBroadcastsLocked(); if (anrMessage != null) { //【見小節2.2.6】 mHandler.post(new AppNotResponding(app, anrMessage)); } }
[-> BroadcastQueue.java]
private final class AppNotResponding implements Runnable { ... public void run() { //【見小節3.1】 mService.appNotResponding(mApp, null, null, false, mAnnotation); } }
public void appNotRespondingViaProvider(IBinder connection) { enforceCallingPermission( android.Manifest.permission.REMOVE_TASKS, "appNotRespondingViaProvider()"); final ContentProviderConnection conn = (ContentProviderConnection) connection; if (conn == null) { return; } final ProcessRecord host = conn.provider.proc; //沒法找到provider所處的進程 if (host == null) { return; } final long token = Binder.clearCallingIdentity(); try { //【見小節3.1】 appNotResponding(host, null, null, false, "ContentProvider not responding"); } finally { Binder.restoreCallingIdentity(token); } }
Timeout時間20s
調用鏈:
ContentProviderClient.NotRespondingRunnable.run ContextImpl.ApplicationContentResolver.appNotRespondingViaProvider ActivityThread.appNotRespondingViaProvider AMP.appNotRespondingViaProvider AMS.appNotRespondingViaProvider
在native層InputDispatcher.cpp中通過層層調用,此處先省略過程,後續再展開,從native層com_android_server_input_InputManagerService調用到java層InputManagerService。
[-> InputManagerService.java]
private long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) { //【見小節2.4.2】 return mWindowManagerCallbacks.notifyANR( inputApplicationHandle, inputWindowHandle, reason); }
mWindowManagerCallbacks爲InputMonitor對象
[-> InputMonitor.java]
public long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) { AppWindowToken appWindowToken = null; WindowState windowState = null; boolean aboveSystem = false; synchronized (mService.mWindowMap) { if (inputWindowHandle != null) { windowState = (WindowState) inputWindowHandle.windowState; if (windowState != null) { appWindowToken = windowState.mAppToken; } } if (appWindowToken == null && inputApplicationHandle != null) { appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken; } //輸出input事件分發超時log if (windowState != null) { Slog.i(WindowManagerService.TAG, "Input event dispatching timed out " + "sending to " + windowState.mAttrs.getTitle() + ". Reason: " + reason); int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); aboveSystem = windowState.mBaseLayer > systemAlertLayer; } else if (appWindowToken != null) { Slog.i(WindowManagerService.TAG, "Input event dispatching timed out " + "sending to application " + appWindowToken.stringName + ". Reason: " + reason); } else { Slog.i(WindowManagerService.TAG, "Input event dispatching timed out " + ". Reason: " + reason); } mService.saveANRStateLocked(appWindowToken, windowState, reason); } if (appWindowToken != null && appWindowToken.appToken != null) { //【見小節2.5.1】 boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason); if (! abort) { return appWindowToken.inputDispatchingTimeoutNanos; } } else if (windowState != null) { //AMP通過binder,最終調用到AMS【見小節2.4.3】 long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut( windowState.mSession.mPid, aboveSystem, reason); if (timeout >= 0) { return timeout * 1000000L; //轉化爲納秒 } } return 0; }
public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { ... ProcessRecord proc; long timeout; synchronized (this) { synchronized (mPidsSelfLocked) { proc = mPidsSelfLocked.get(pid); //根據pid查看進程record } timeout = getInputDispatchingTimeoutLocked(proc); } //【見小節2.4.4】 if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) { return -1; } return timeout; }
inputDispatching的超時爲KEY_DISPATCHING_TIMEOUT
,即timeout = 5s
public boolean inputDispatchingTimedOut(final ProcessRecord proc, final ActivityRecord activity, final ActivityRecord parent, final boolean aboveSystem, String reason) { ... final String annotation; if (reason == null) { annotation = "Input dispatching timed out"; } else { annotation = "Input dispatching timed out (" + reason + ")"; } if (proc != null) { ... mHandler.post(new Runnable() { public void run() { //【見小節3.1】 appNotResponding(proc, activity, parent, aboveSystem, annotation); } }); } return true; }
調用鏈:
InputManagerService.notifyANR InputMonitor.notifyANR AMP.inputDispatchingTimedOut AMS.inputDispatchingTimedOut
keyDispatching timout與inputDispatching Timeout流暢基本一致。
調用鏈:
InputManagerService.notifyANR InputMonitor.notifyANR ActivityRecord.Token.keyDispatchingTimedOut AMS.inputDispatchingTimedOut
[-> ActivityRecord.java]
final class ActivityRecord { static class Token extends IApplicationToken.Stub { public boolean keyDispatchingTimedOut(String reason) { ActivityRecord r; ActivityRecord anrActivity; ProcessRecord anrApp; synchronized (mService) { r = tokenToActivityRecordLocked(this); if (r == null) { return false; } anrActivity = r.getWaitingHistoryRecordLocked(); anrApp = r != null ? r.app : null; } return mService.inputDispatchingTimedOut(anrApp, anrActivity, r, false, reason); } ... } }
對於keyDispatching Timeout的ANR,當觸發該類型ANR時,若是再也不有輸入事件,則不會彈出ANR對話框;只有在下一次input事件產生後5s才彈出ANR提示框。
[-> ActivityManagerService.java]
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); if (mController != null) { try { // 0 == continue, -1 = kill process immediately int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation); if (res < 0 && app.pid != MY_PID) { app.kill("anr", true); } } catch (RemoteException e) { mController = null; Watchdog.getInstance().setActivityController(null); } } long anrTime = SystemClock.uptimeMillis(); if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); } synchronized (this) { // PowerManager.reboot() 會阻塞很長時間,所以忽略關機時的ANR if (mShuttingDown) { Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); return; } else if (app.notResponding) { Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); return; } else if (app.crashing) { Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); return; } app.notResponding = true; //記錄ANR EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, app.processName, app.info.flags, annotation); // Dump thread traces as quickly as we can, starting with "interesting" processes. firstPids.add(app.pid); int parentPid = app.pid; if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid; if (parentPid != app.pid) firstPids.add(parentPid); if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID); for (int i = mLruProcesses.size() - 1; i >= 0; i--) { ProcessRecord r = mLruProcesses.get(i); if (r != null && r.thread != null) { int pid = r.pid; if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) { if (r.persistent) { firstPids.add(pid); } else { lastPids.put(pid, Boolean.TRUE); } } } } } //輸出ANR到main log. StringBuilder info = new StringBuilder(); info.setLength(0); info.append("ANR in ").append(app.processName); if (activity != null && activity.shortComponentName != null) { info.append(" (").append(activity.shortComponentName).append(")"); } info.append("\n"); info.append("PID: ").append(app.pid).append("\n"); if (annotation != null) { info.append("Reason: ").append(annotation).append("\n"); } if (parent != null && parent != activity) { info.append("Parent: ").append(parent.shortComponentName).append("\n"); } final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); //dump棧信息 File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids, NATIVE_STACKS_OF_INTEREST); String cpuInfo = null; if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); synchronized (mProcessCpuTracker) { //輸出各個進程的CPU使用狀況 cpuInfo = mProcessCpuTracker.printCurrentState(anrTime); } //輸出CPU負載 info.append(processCpuTracker.printCurrentLoad()); info.append(cpuInfo); } info.append(processCpuTracker.printCurrentState(anrTime)); Slog.e(TAG, info.toString()); if (tracesFile == null) { //發送signal 3來dump棧信息 Process.sendSignal(app.pid, Process.SIGNAL_QUIT); } //將anr信息添加到dropbox addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null); if (mController != null) { try { // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately int res = mController.appNotResponding(app.processName, app.pid, info.toString()); if (res != 0) { if (res < 0 && app.pid != MY_PID) { app.kill("anr", true); } else { synchronized (this) { mServices.scheduleServiceTimeoutLocked(app); } } return; } } catch (RemoteException e) { mController = null; Watchdog.getInstance().setActivityController(null); } } boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; synchronized (this) { mBatteryStatsService.noteProcessAnr(app.processName, app.uid); if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { app.kill("bg anr", true); return; } // Set the app's notResponding state, and look up the errorReportReceiver makeAppNotRespondingLocked(app, activity != null ? activity.shortComponentName : null, annotation != null ? "ANR " + annotation : "ANR", info.toString()); //彈出ANR對話框 Message msg = Message.obtain(); HashMap<String, Object> map = new HashMap<String, Object>(); msg.what = SHOW_NOT_RESPONDING_MSG; msg.obj = map; msg.arg1 = aboveSystem ? 1 : 0; map.put("app", app); if (activity != null) { map.put("activity", activity); } mUiHandler.sendMessage(msg); } }
主要發送ANR, 則會輸出
UI線程,例如:
ANR分析:須要關注CPU/IO,trace死鎖等數據。