1. 程式人生 > >基於android7.0流程分析anr產生原因

基於android7.0流程分析anr產生原因

1.什麼是anr

ANR(Application Not Responding),即“應用程式無響應”。anr是android自身的一種監測機制,如果ui執行緒在特定時間無法對輸入事件做出響應或則對特定操作沒有執行完畢,就會出現anr的情況。

2.anr分類

1,InputEventTimeout

輸入事件(按鍵或觸屏事件)超過5s無響應就會彈出anr提示框,供使用者選擇繼續等待程式響應或則關閉這個應用程式,輸入事件無響應是anr出現最頻繁的情況,輸入超時類可以細分以下二類:

1.處理訊息超時,在log中會出現Input dispatching timed out 

2.無法獲取焦點,這一類通常是由於新視窗建立慢或者舊視窗退出慢而導致視窗無法獲取焦點而出現anr,在log中一般會出現 Reason: Waiting because no window hasfocus but there is a focused application that may eventually add a window when it finishes starting up.

2.BroadcastTimeout

 BroadcastRecevier 在規定時間無法處理完成(前臺廣播10s,後臺廣播60s),這類超時沒有提示框彈出,一般出現在statusbar,setting應用中

3.ServiceTimeout

 service在20s內無法處理完成操作,即會報出服務超時,同樣沒有提示框出現,這一類anr可能會出現在bluetooth或wifi中,很少會遇到,至少我沒有遇到過

3.anr產生的原因

 產生anr的原因很多,可以分為二類:

1.應用自身程序引起的問題,比如, 在oncreate ,onstart等生命週期中執行耗時操作,ui執行緒阻塞,掛起,死迴圈等

2,其他程序引起的,比如:io操作導致cpu使用過高,導致當前應用程序無法搶佔到cpu時間片


4.anr產生過程

1.keyDispatchingTimeOut (5s)

當應用程式的Window處於Active狀態並且能夠接收輸入事件(例如按鍵事件、觸控事件等)時,系統底層上報的事件就會被InputDispatcher分發給這個應用程式,應用程式的主執行緒通過InputChannel讀取輸入事件並交給介面檢視處理:

InputReader:讀事件,待有事件發生時喚醒InputDispatcher
InputDispatcher:將事件派發給當前活動的視窗。
InputDispatcherThread::threadLoop-------->
InputDispatcher::dispatchOnce------>
InputDispatcher::dispatchOnceInnerLocked---->
分支1:(key事件)
InputDispatcher::dispatchKeyLocked------------〉
InputDispatcher::findFocusedWindowTargetsLocked-----
分支2:(螢幕觸控和移動事件)                                    
InputDispatcher::dispatchMotionLocked-------->
InputDispatcher ::findTouchedWindowTargetsLocked----

InputDispatcher::handleTargetsNotReadyLocked----->
InputDispatcher::onANRLocked------------>
InputDispatcher::doNotifyANRLockedInterruptible----->

com_android_server_input_InputManagerService::NativeInputManager::notifyANR--->

InputMangerService::notifyANR---->
InputMonitor::notifyANR-->
ActivityRecord::keyDispatchingTimedOut-〉

ActivityManageService::inputDispatchingTimedOut 而後就會在System.log,dropbox,trane.txt中看出相應的anr資訊輸出

 分支1: Key訊息處理中findFocusedWindowTargetsLocked是ANR 的入口函式.從這裡可以看出在三種情況下會進入ANR.
1:如果該視窗沒找到,但應用程式找到,說明此時該應用程式正在啟動中,還沒有完成。等待直到啟動完成或超時,超 時就 anr.
2:判斷該視窗是否pause狀態,是則等待,直到啟動完成或超時,超時就 anr
3:判斷該視窗是否已經處理完上一個分發給它的事件,沒有就等待,直到超時anr
在按鍵訊息傳遞給View的過程中會呼叫ViewRootImpl.java中deliverKeyEvent.這裡進行了一些處理.如果存在輸入法視窗,會先將按鍵訊息派發到輸入法視窗處理,輸入法如果沒有消耗訊息,訊息則進入View樹進行處理。 在訊息傳遞中,完成訊息處理後,都會呼叫finishKeyEvent.給Wms一個處理回執,從而使得Wms可以繼續派發下一個訊息.
分支2:Motion訊息處理中findTouchedWindowTargetsLocked是ANR 的入口函式. 從這裡可以看出主要存在以下三種情況會進入ANR.
1: 如果該視窗沒找到,但應用程式找到,則等待新增新視窗。等待完成,超 時就 anr
2: 判斷該視窗是否pause狀態, 是則等待,超時就 anr

3: 判斷該視窗是否已經處理完上一個分發給它的事件,沒有就等待,直到超時anr

2、BroadcastTimeout(一般是前臺廣播10s,後臺廣播60s) BroadcastReceiver在特定時間內無法處理完成;
呼叫流程如下:
ActivityManagerService.java
attachApplication-->attachApplicationLocked
對於broadcast的anr
-->sendPendingBroadcastsLocked(app);
BroadcastQueue.java中
-->processCurBroadcastLocked(br, app);丟擲異常時,捕獲異常時會呼叫scheduleBroadcastsLocked會發送BROADCAST_INTENT_MSG到訊息佇列,handleMessage函式來處理此訊息呼叫processNextBroadcast(true)被呼叫來分發廣播,這呼叫setBroadcastTimeoutLocked(timeoutTime);來註冊超時的廣播,BROADCAST_TIMEOUT_MSG訊息傳送到超時的訊息佇列中,同樣通過handleMessage函式來處理此訊息,並呼叫broadcastTimeoutLocked函式,若發生超時則呼叫mHandler.post(new AppNotResponding(app, anrMessage))觸發ANR的報告發生;


3、ServiceTimeout(一般是20 s,後臺service為200s) Service在特定的時間內無法處理完成。
static final int SERVICE_TIMEOUT = 20*1000;呼叫流程如下:
ActivityManagerService.java
attachApplication
attachApplicationLocked
對於service的anr
mServices.attachApplicationLocked
ActiveServices.java

realStartServiceLocked->bumpServiceExecutingLocked->scheduleServiceTimeoutLocked這裡會發出SERVICE_TIMEOUT_MSG超時訊息ActivityManagerService.java中的Handler mHandler內的handleMessage會處理這個訊息呼叫mServices.serviceTimeout((ProcessRecord)msg.obj);說明service程式超時20s會發生ANR呼叫mAm.appNotResponding(proc, null, null, false, anrMessage)觸發ANR發生;

5.anr log的輸出程式碼分析

AppErrors-->appNotResponding

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 (mService.mController != null) {
            try {
                // 0 == continue, -1 = kill process immediately
                int res = mService.mController.appEarlyNotResponding(
                        app.processName, app.pid, annotation);
                if (res < 0 && app.pid != MY_PID) {
                    app.kill("anr", true);
                }
            } catch (RemoteException e) {
                mService.mController = null;
                Watchdog.getInstance().setActivityController(null);
            }
        }

        long anrTime = SystemClock.uptimeMillis();
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
    //更新cpu使用率
            mService.updateCpuStatsNow();
        }

        // Unless configured otherwise, swallow ANRs in background processes & kill the process.
        boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;


        boolean isSilentANR;


        synchronized (mService) {
             //過濾幾種不需要顯示anr的情況
            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
            if (mService.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;
            }

            // In case we come through here for the same app before completing
            // this one, mark as anring now so we will bail out.
            app.notResponding = true;

            // Log the ANR to the event log.
            //輸出相關anr資訊到event.log中
            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);

            // Don't dump other PIDs if it's a background ANR
            isSilentANR = !showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID;
            if (!isSilentANR) {
                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 = mService.mLruProcesses.size() - 1; i >= 0; i--) {
                    ProcessRecord r = mService.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);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
                            } else {
                                lastPids.put(pid, Boolean.TRUE);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
                            }
                        }
                    }
                }
            }
        }


        // Log the ANR to the main log.
        //輸出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");
        }

        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);

        String[] nativeProcs = NATIVE_STACKS_OF_INTEREST;
        // don't dump native PIDs for background ANRs
        File tracesFile = null;
        //輸入trace檔案
        if (isSilentANR) {
            tracesFile = mService.dumpStackTraces(true, firstPids, null, lastPids,
                null);
        } else {
            tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
                nativeProcs);
        }


        String cpuInfo = null;
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            //重新整理cpu的使用情況
            mService.updateCpuStatsNow();
            synchronized (mService.mProcessCpuTracker) {
                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
            }
            //輸出出現anr之前cpu的使用率
            info.append(processCpuTracker.printCurrentLoad());
            info.append(cpuInfo);
        }
        //輸出出現anr之後cpu的使用率
        info.append(processCpuTracker.printCurrentState(anrTime));


        Slog.e(TAG, info.toString());
        if (tracesFile == null) {
            // There is no trace file, so dump (only) the alleged culprit's threads to the log
            Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
        }
        //輸出相關的anr資訊到dropbox中
        mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
                cpuInfo, tracesFile, null);

        if (mService.mController != null) {
            try {
                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
                int res = mService.mController.appNotResponding(
                        app.processName, app.pid, info.toString());
                if (res != 0) {
                    if (res < 0 && app.pid != MY_PID) {
                        app.kill("anr", true);
                    } else {
                        synchronized (mService) {
                            mService.mServices.scheduleServiceTimeoutLocked(app);
                        }
                    }
                    return;
                }
            } catch (RemoteException e) {
                mService.mController = null;
                Watchdog.getInstance().setActivityController(null);
            }
        }

        synchronized (mService) {
            mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);

            if (isSilentANR) {
                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());

            // Bring up the infamous App Not Responding dialog
            //顯示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);
        }
    }


6.如何防止和分析anr

1.預防出現anr

ui主執行緒中只做ui相關的處理,避免過多執行耗時操作,如遇到需要執行的耗時操作,建議放到單獨的執行緒中進行處理,儘量使用handler來處理ui執行緒和其他執行緒之間的互動

  2.anr一般分析步驟

   1.首先從main.log找到程序出現anr對應的大體時間,如在log中查詢"anr in"欄位

   2.根據出現anr的程序名到anr資料夾中找出trance.txt檔案,根據檔案的資訊首先判斷anr的型別,是app自身還是系統問題,如果是app應用問題,則根據對用的呼叫解決問題

   3.如不是app應用問題,則從main.log中判斷anr的型別,如是keydispatch time out,則應該根據anr準確的時間點上推5s鍾,看看此時對用程序正在程序何操作(具體anr的準確時間可以在event.log中搜索anr)

    4.trace中無明顯異常,可以從下面的情況考慮

        是否由於io,資料庫處理導致cpu使用率過高從而導致其他應用程序無法搶佔cpu時間片

   是否是低記憶體導致anr(如低記憶體,可以從system.log中檢視程序被kill, 輸入某某程序died)

是否由於輸入法互動處理不當導致不能返回出現anr

是否由於程序鎖等待,死鎖情況出現anr

相關推薦

基於android7.0流程分析anr產生原因

1.什麼是anr ANR(Application Not Responding),即“應用程式無響應”。anr是android自身的一種監測機制,如果ui執行緒在特定時間無法對輸入事件做出響應或則對特定操作沒有執行完畢,就會出現anr的情況。 2.anr分類 1,Input

Android7.0原始碼分析之Binder——Client分析

Binder  Client分析,咋一看,就那麼四個關鍵方法:getService()、addService()、checkService()、listServices()。四個方法原理都差不多,以下僅

Android ANR產生原因和解決辦法

1、主執行緒中,程式應該儘量少進行程式執行,Activity應該在它的關鍵生命週期方法(如onCreate()和onResume())裡儘可能少的去做建立操作。(可以採用重新開啟子執行緒的方式,然後使用Handler+Message的方式做一些操作,比如更新主執行緒中的ui等) 2、應用程式應該避免在Broa

android ANR產生原因(三總常見型別)和解決辦法

轉載:http://blog.csdn.net/lonlylove/article/details/42006285 記得好久之前,面試時候,面試官問我,造成ANR的原因有哪些?   答:  自我感覺良好的我,就說了好多(包括了經典中兩個),最後面試官說了句,其實就三

[Android7.0]NFC初始化的流程分析

1、NFC初始化的時序圖: 2、程式碼分析: 初始化分兩部分,第一供應framework使用的服務端初始化,並將服務新增到ServiceManager中,第二是初始化NFC介面卡NfcAdapter,其中就包含何種對應NFC協議的服務。 * 服務端的初

Android7.0去電流程原始碼分析(一)

2.去電從撥號盤介面有關撥號的部分由DialpadFragment.java實現,無論是單卡還是雙卡,當點選撥號按鍵時,最後都會呼叫handleDialButtonPressed方法進行處理,DialogFragmentCall_Action的活動Call_Ac

Android7.0關機流程分析

在長按power鍵時系統會彈出對話方塊,讓使用者選擇關機, 重啟或者其他模式. 在本文中重點講解系統關機流程. 讓大家瞭解在系統關機過程都做了哪些事情,而導致關機慢又有那些主要的原因.在一文中有對按power鍵傳輸講解, 長按power鍵是在java層的PhoneWind

ANR產生原因及其定位分析

前言 ANR是Android 中獨有的概念,全稱Application No Responding 如何定位和和避免出現這個問題是Android程式設計師的必備修養 一 、ANR產生的原因 1.1 ANR 產生的原因 只有當應用程式的UI

Android ANR產生原因以及其定位分析

前言    ANR是Android中一個獨有的概念,它的全稱是Application Not Responding(應用程式無響應)。    相信從事Android開發的同學,或多或少都遇到過,對於

0移植uboot (二) _啟動流程分析

title tco ret 沒有 返回 ips css location config 來源:Linux社區 作者:xiaojiang1025 : http://www.linuxidc.com/Linux/2017-02/141019.htm 經過

Android7.0 MediaRecorder源碼分析

錄像 stage sdn ice ren oop 新建 n) edi 最近在做Camera的方案(雙進程打開一個Camera),涉及到使用MediaRecorder 進行錄像,還是自己新建一套錄像系統。接下來我將記錄下本次源碼分析的流程。  有關於Client和Server

第一次作業 基於Linux 0.12的進程模型分析

schedule 基於 參考 函數 子進程 count node block font 作業內容 挑選一個開源的操作系統,深入源碼分析其進程模型,具體包含如下內容: 操作系統是怎麽組織進程的 進程狀態如何轉換(給出進程狀態轉換圖) 進程是如何調度的 談談自己對該操作系統進

linux 3.0.8 alsa資料流程分析

ALSA開啟資料流程      soc_pcm_open => cpu_dai->driver->ops->startup => platform->driver->ops->open => co

Tomcat 原始碼分析 WebappClassLoader 分析 (基於8.0.5)

0. 疑惑 在剛接觸 Tomcat 中的ClassLoader時心中不免冒出的疑惑: "Tomcat 裡面是怎麼樣設計ClassLoader的, 這樣設計有什麼好處?"; 我們先把這個問題留著, 到最後在看 ! 1. Java 中 ClassLoader 類別 1. BootstrapC

bug產生原因分析

目錄 一、前後端使用架構導致 二、開發人員經驗問題/思維嚴謹性導致 三、業務特點導致 四、測試人員的經驗缺乏導致 五、迭代週期不合理導致 六、上下游業務嚴重耦合導致 前言           產生bug的具體原因或

Android9.0 Activity啟動流程分析(二)

文章目錄 1、ActivityThread的main函式 2. AMS的attachApplication函式 2.1 Part-I 2.2 Part-II 2.2.1 ApplicationThread的bindApp

Android9.0 Activity啟動流程分析(一)

1、ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay介紹   本篇文章是基於Android refs/tags/android-9.0.0_r8分支的程式碼進行分析的   在分析Activity啟動的原始碼之前先介紹一下Act

android Telephony學習 --- 第七篇 android7.0 來電(MT)流程

我們先看下7.0來電大體流程: ##Framework modem接收到來電通知訊息後,以AT指令的方式上報RIL層,RIL層通過sokcet將訊息傳送給RILJ, 上報事件ID: RIL_UNSOL

深入bug產生原因分析(二)那些耽誤了測試時間的無效bug

前言              之前寫深入bug產生原因分析(一),這裡主要從實際的專案bug中,總結出了實際bug的根源的幾個方面。這些問題的解決不但沒有對最終交付的產品有所裨益,反而耽誤了不少測試時間。現在就簡要做下梳理,

Android6.0 Reset恢復出廠設定流程分析

點選Settings應用中的恢復出廠設定按鈕後流程分析:先使用grep命令搜尋"恢復出廠設定"字串,找到相應的佈局檔案: packages/apps/Settings/res/xml/privacy_settings.xml <PreferenceScree