原 Android中ANR的監測與定位

1、原理android

1. ANR監測原理ide

    判斷ANR的方法其實很簡單,咱們在子線程裏向主線程發消息,若是過了固定時間後,消息仍未處理,則說明已發生ANR了。oop

    看懂了直接看2,沒看懂繼續看。.net

    Android應用程序的全部交互操做和響應,都是經過主線程的消息機制來進行的。例如當用戶點擊了某個Button,系統會向主線程發送消息,主線程的Looper從主線程消息隊列中取出消息並處理,處理完當前消息,主線程Looper再去取出下一個消息。當主線程作了耗時的任務,主線程的Looper就沒法從消息隊列中取出新的消息,所表現出的就是程序卡頓,甚至是ANR。同理,咱們在子線程往主線程發送一個消息,要是消息沒法獲得及時處理,那說明程序發生ANR了。線程


2. 定位耗時操做的原理3d

當程序ANR後,咱們能夠經過主線程Looper拿到主線程Thread,而後經過getStackTrace拿到主線程當前的調用棧,從而定位到發生ANR的地方,定位到耗時操做。blog

2、代碼實現隊列


1. 首先咱們定義一個線程,用來監測主線程。get

    在該線程中,咱們首先給主線程發送消息,而後睡眠指定時間,以後監測消息是否被處理,若未被處理,則拋出ANR異常。消息隊列

    爲何叫ANRWatchDog:瞭解嵌入式的人對看門狗應該很熟悉,在嵌入式中,看門狗定時器在程序跑飛時,可定時復位程序,而咱們必須按期喂狗(將定時器清零),表示程序正常運行。

    watchDogHandler:用來給主線程發送消息,並處理消息。

lastTimeTick/timeTick:用來判斷消息是否被處理

 

            public class ANRWatchDog extends Thread {
                public static final int MESSAGE_WATCHDOG_TIME_TICK = 0;
                /**
                 * 斷定Activity發生了ANR的時間,必需要小於5秒,不然等彈出ANR,可能就被用戶當即殺死了。
                 */
                public static final int ACTIVITY_ANR_TIMEOUT = 2000;
     
     
                private static int lastTimeTick = -1;
                private static int timeTick = 0;
     
     
                private Handler watchDogHandler = new android.os.Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        timeTick++;
                        timeTick = timeTick % Integer.MAX_VALUE;
                    }
                };
                @Override
                public void run() {
                    while (true) {
                        watchDogHandler.sendEmptyMessage(MESSAGE_WATCHDOG_TIME_TICK);
                        try {
                            Thread.sleep(ACTIVITY_ANR_TIMEOUT);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //若是相等,說明過了ACTIVITY_ANR_TIMEOUT的時間後watchDogHandler仍沒有處理消息,已經ANR了
                        if (timeTick == lastTimeTick) {
                            throw new ANRException();
                        } else {
                            lastTimeTick = timeTick;
                        }
                    }
                }
            }

 

2. 啓動這個線程

爲了確保該線程在程序啓動後第一時間運行,所以自定義一個Application,在onCreate中開啓這個線程。


        public class MyApplication extends Application {
            @Override
            public void onCreate() {
                new ANRWatchDog().start();
                super.onCreate();
            }
        }

 

 3. 定義發生ANR時的操做,這裏是自定義了一個異常,ANR時拋出。


        public class ANRException extends RuntimeException {
            public ANRException() {
                super("應用程序無響應,快來改BUG啊!!");
                Thread mainThread = Looper.getMainLooper().getThread();
                setStackTrace(mainThread.getStackTrace());
            }
        }

 

4. 在Activity中模擬耗時操做

在個人demo中是直接Thread.sleep(10000),讓主線程睡10秒。


5. Manifest文件中,註冊Activity,配置Application

 

 

3、運行結果

程序過了幾秒後拋出了ANRException,以下圖所示。箭頭指的地方就是產生ANR的地方(耗時操做),在本程序中是Thread.sleep。


代碼下載戳這裏  

相關文章
相關標籤/搜索