<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [TOC] ## 簡介 ANR(Application Not Responding),應用程序無響應,簡單一個定義,卻涵蓋了很多Android系統的設計思想。 ANR由消息處理機制保證,Android在系統層實現了一套精密的機制來發現ANR,核心原理是 **消息調度** 和 **超時處理** 。 造成ANR的場景有 * Service Timeout:比如前臺服務在20s內未執行完成; * BroadcastQueue Timeout:比如前臺廣播在10s內未執行完成 * ContentProvider Timeout:內容提供者,在publish過超時10s; * InputDispatching Timeout: 輸入事件分發超時5s,包括按鍵和觸摸事件。 ## ANR監測機制 ANR監測機制包含三種: * **Service ANR**,前臺進程中Service生命周期不能超過20秒,后臺進程中Service的生命周期不能超過200秒。 在啟動Service時,拋出定時消息SERVICE\_TIMEOUT\_MSG或SERVICE\_BACKGOURND\_TIMEOUT\_MSG,如果定時消息響應了,則說明發生了ANR * **Broadcast ANR**,前臺的“串行廣播消息”必須在10秒內處理完畢,后臺的“串行廣播消息”必須在60秒處理完畢, 每派發串行廣播消息到一個接收器時,都會拋出一個定時消息BROADCAST\_TIMEOUT\_MSG,如果定時消息響應,則判斷是否廣播消息處理超時,超時就說明發生了ANR * **Input ANR**,輸入事件必須在5秒內處理完畢。在派發一個輸入事件時,會判斷當前輸入事件是否需要等待,如果需要等待,則判斷是否等待已經超時,超時就說明發生了ANR ## Service處理超時源碼介紹 Service運行在應用程序的主線程,如果Service的執行時間超過20秒,則會引發ANR。 當發生Service ANR時,一般可以先排查一下在Service的生命周期函數中(onCreate(), onStartCommand()等)有沒有做耗時的操作,譬如復雜的運算、IO操作等。 如何檢測Service超時呢?Android是通過設置定時消息實現的。定時消息是由AMS的消息隊列處理的(system\_server的ActivityManager線程)。 AMS有Service運行的上下文信息,所以在AMS中設置一套超時檢測機制也是合情合理的。 Service ANR機制相對最為簡單,主體實現在[ActiveServices.java,**realStartServiceLocked** 中。 當Service的生命周期開始時,**bumpServiceExecutingLocked** 會被調用,緊接著會調**scheduleServiceTimeoutLocked**ActiveServices.java ### 埋炸彈 ActiveServices(mAm.mHandler ) ~~~ 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) { mAm.appDiedLocked(app); throw e; } finally { ... } } 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.3.1】 mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT)); } ~~~ ### 拆炸彈 ActivityThread.java ~~~ 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(); //拆除炸彈引線[見小節2.2.2] ActivityManagerNative.getDefault().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (Exception e) { ... } } ~~~ ### 引爆炸彈 ~~~ final class MainHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case SERVICE_TIMEOUT_MSG: { ... //【見小節2.3.2】 mServices.serviceTimeout((ProcessRecord)msg.obj); } break; ... } ... } } ~~~ serviceTimeout ~~~ 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 mAm.appNotResponding(proc, null, null, false, anrMessage); } ~~~ ## Input處理超時 ![](https://img.kancloud.cn/a6/9f/a69fd2570e083d83ac74e55bfd11411f_863x231.png) 內核將原始事件寫入到設備節點中,InputReader在其線程循環中不斷地從EventHub中抽取原始輸入事件,進行加工處理后將加工所得的事件放入InputDispatcher的派發發隊列中。InputDispatcher則在其線程循環中將派發隊列中的事件取出,查找合適的窗口,將事件寫入到窗口的事件接收管道中。窗口事件接收線程的Looper從管道中將事件取出,交由窗口事件處理函數進行事件響應。關鍵流程有:原始輸入事件的讀取與加工;輸入事件的派發;輸入事件的發送、接收與反饋。其中輸入事件派發是指InputDispatcher不斷的從派發隊列取出事件、尋找合適的窗口進行發送的過程,輸入事件的發送是InputDispatcher通過Connection對象將事件發送給窗口的過程。 InputDispatcher與窗口之間的跨進程通信主要通過InputChannel來完成。在InputDispatcher與窗口通過InputChannel建立連接之后,就可以進行事件的發送、接收與反饋;輸入事件的發送和接收主要流程如圖所示 InputDispatcher作為中樞,不停地在遞送著輸入事件,當一個事件無法得到處理的時候,InputDispatcher不能就此死掉啊,否則系統也太容易崩潰了。?**InputDispatcher的策略是放棄掉處理不過來的事件,并發出通知(這個通知機制就是ANR)**,繼續進行下一輪消息的處理。 事件分發5s限制定義在InputDispatcher.cppInputDispatcher::handleTargetsNotReadyLocked方法中如果事件5s之內還沒有分發完畢,則調用InputDispatcher::onANRLocked()提示用戶應用發生ANR; ~~~ //默認分發超時間為5s const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { // 1.如果當前沒有聚焦窗口,也沒有聚焦的應用 if (applicationHandle == NULL && windowHandle == NULL) { ... } else { // 2.有聚焦窗口或者有聚焦的應用 if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { // 獲取等待的時間值 if (windowHandle != NULL) { // 存在聚焦窗口,DEFAULT_INPUT_DISPATCHING_TIMEOUT事件為5s timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else if (applicationHandle != NULL) { // 存在聚焦應用,則獲取聚焦應用的分發超時時間 timeout = applicationHandle->getDispatchingTimeout( DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else { // 默認的分發超時時間為5s timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; } } } // 如果當前時間大于輸入目標等待超時時間,即當超時5s時進入ANR處理流程 // currentTime 就是系統的當前時間,mInputTargetWaitTimeoutTime 是一個全局變量, if (currentTime >= mInputTargetWaitTimeoutTime) { // 調用ANR處理流程 onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, mInputTargetWaitStartTime, reason); // 返回需要等待處理 return INPUT_EVENT_INJECTION_PENDING; } } ~~~ 當應用主線程被卡住的事件,再點擊該應用其它組件也是無響應,因為事件派發是串行的,上一個事件不處理完畢,不會處理下一個事件。 Activity.onCreate執行耗時操作,不管用戶如何操作都不會發生ANR,因為輸入事件相關監聽機制還沒有建立起來;InputChannel通道還沒有建立這時是不會響應輸入事件,InputDispatcher還不能事件發送到應用窗口,ANR監聽機制也還沒有建立,所以此時是不會報告ANR的。 ## ANR的報告機制 無論哪種類型的ANR發生以后,最終都會調用?AMS.appNotResponding()?方法,所謂“殊途同歸”。這個方法的職能就是向用戶或開發者報告ANR發生了。 最終的表現形式是:彈出一個對話框,告訴用戶當前某個程序無響應;輸入一大堆與ANR相關的日志,便于開發者解決問題。 最終形式我們見過很多,但輸出日志的原理是什么,未必所有人都了解,下面我們就來認識一下是如何輸出ANR日志 ~~~ final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) { ... if (ActivityManagerService.MONITOR_CPU_USAGE) { // 1. 更新CPU使用信息。ANR的第一次CPU信息采樣,采樣數據會保存在mProcessStats這個變量中 mService.updateCpuStatsNow(); } // 記錄ANR到EventLog中 EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, app.processName, app.info.flags, annotation); // 輸出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"); } // 3. 打印調用棧。具體實現由dumpStackTraces()函數完成 File tracesFile = ActivityManagerService.dumpStackTraces( true, firstPids, (isSilentANR) ? null : processCpuTracker, (isSilentANR) ? null : lastPids, nativePids); String cpuInfo = null; // MONITOR_CPU_USAGE默認為true if (ActivityManagerService.MONITOR_CPU_USAGE) { // 4. 更新CPU使用信息。ANR的第二次CPU使用信息采樣。兩次采樣的數據分別對應ANR發生前后的CPU使用情況 mService.updateCpuStatsNow(); synchronized (mService.mProcessCpuTracker) { // 輸出ANR發生前一段時間內各個進程的CPU使用情況 cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime); } // 輸出CPU負載 info.append(processCpuTracker.printCurrentLoad()); info.append(cpuInfo); } // 輸出ANR發生后一段時間內各個進程的CPU使用率 info.append(processCpuTracker.printCurrentState(anrTime)); //會打印發生ANR的原因,如輸入事件導致ANR的不同場景 Slog.e(TAG, info.toString()); if (tracesFile == null) { // There is no trace file, so dump (only) the alleged culprit's threads to the log // 發送signal 3(SIGNAL_QUIT)來dump棧信息 Process.sendSignal(app.pid, Process.SIGNAL_QUIT); } // 將anr信息同時輸出到DropBox mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null); // Bring up the infamous App Not Responding dialog // 5. 顯示ANR對話框。拋出SHOW_NOT_RESPONDING_MSG消息, // AMS.MainHandler會處理這條消息,顯示AppNotRespondingDialog對話框提示用戶發生ANR Message msg = Message.obtain(); HashMap<String, Object> map = new HashMap<String, Object>(); msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = map; msg.arg1 = aboveSystem ? 1 : 0; map.put("app", app); if (activity != null) { map.put("activity", activity); } mService.mUiHandler.sendMessage(msg); } ~~~ 該方法的主體邏輯可以分成五個部分來看: * 更新CPU的統計信息。這是發生ANR時,第一次CPU使用信息的采樣,采樣數據會保存在mProcessStats這個變量中 * 填充firstPids和lastPids數組。當前發生ANR的應用會首先被添加到firstPids中,這樣打印函數棧的時候,當前進程總是在trace文件的最前面 * 打印函數調用棧(StackTrace)。具體實現由dumpStackTraces()函數完成 * 更新CPU的統計信息。這是發生ANR時,第二次CPU使用信息的采樣,兩次采樣的數據分別對應ANR發生前后的CPU使用情況 * 顯示ANR對話框。拋出SHOW\_NOT\_RESPONDING\_MSG消息,AMS.MainHandler會處理這條消息,顯示AppNotRespondingDialog 當然,除了主體邏輯,發生ANR時還會輸出各種類別的日志: * event log,通過檢索”am\_anr”關鍵字,可以找到發生ANR的應用 * main log,通過檢索”ANR in “關鍵字,可以找到ANR的信息,日志的上下文會包含CPU的使用情況 * dropbox,通過檢索”anr”類型,可以找到ANR的信息 * traces, 發生ANR時,各進程的函數調用棧信息 我們分析ANR問題,往往是從main log中的CPU使用情況和traces中的函數調用棧開始。所以,更新CPU的使用信息updateCpuStatsNow()方法和打印函數棧dumpStackTraces()方法,是系統報告ANR問題關鍵所在。 ## 參考資料 [ANR機制以及問題分析](https://duanqz.github.io/2015-10-12-ANR-Analysis) [理解Android ANR的觸發原理](http://gityuan.com/2016/07/02/android-anr/)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看