原 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。


代码下载戳这里  

相关文章
相关标签/搜索