(原创)手写一个ANR监控类ANRWatchDog_Android_xiong_st的博客-程序员秘密

技术标签: java  实用工具  android  开发语言  

前言

实际APP开发中,因为主线程阻塞而导致的ANR是一个比较难定位的问题
今天自己写了一个ANR的监控类,用来监听项目中ANR发生的情况
源码后面会贴上来,下面我们进入介绍部分

使用

使用起来很简单
在我们的application里面这样配置即可:

        ANRWatchDog.getInstance().addANRListener(new ANRWatchDog.ANRListener() {
    
            @Override
            public void onAnrHappened(String stackTraceInfo) {
    
                //发生ANR时的回调监听,stackTraceInfo为堆栈信息
            }

            @Override
            public void onNotAnrHappened() {
    
                //未发生ANR
            }
        }).start();

原理介绍

下面是原理介绍部分
首先我们的这个工具类其实是一个单例的线程
可以看到使用的时候调用了start方法来开启线程
线程开启后,我们将线程设置为后台线程
然后进入一个while (true)的死循环
先获取一个当前时间,记录一个标志位
然后用handler往主线程发送一个Runnable消息
然后线程立马进入wait(5000)等待五秒
五秒后,如果没发生卡顿,我们的Runnable肯定就可以运行
然后把标志位改回来,表示未发现ANR即可
如果发生了卡顿,我们只需要通过判断标志位,就可以得知
这时候只需要打印堆栈信息
并且回调监听函数即可
当然,这里设置的默认ANR标准是五秒
我们也可以在工具类里面自己设置
另外,wait(5000)也会偶尔出现一个假唤醒的问题
就是线程被提前唤醒了,并没有真正等待五秒
这里我们也做了预防判断,如果没到五秒,就继续wait,代码如下:
在这里插入图片描述

源码

最后贴上全部的源码,方便大家使用:

public class ANRWatchDog extends Thread {
    

    private static final String TAG = "ANRWatchDog";
    private int timeout = 5000;
    private boolean ignoreDebugger = true;//debug模式下是否监听 true代表debug模式要监听,false代表不监听

    static ANRWatchDog sWatchdog;

    private Handler mainHandler = new Handler(Looper.getMainLooper());


    private class ANRChecker implements Runnable {
    

        private boolean mCompleted;
        private long mStartTime;
        private long executeTime = SystemClock.uptimeMillis();

        @Override
        public void run() {
    
            synchronized (ANRWatchDog.this) {
    
                mCompleted = true;
                executeTime = SystemClock.uptimeMillis();
            }
        }

        void schedule() {
    
            mCompleted = false;
            mStartTime = SystemClock.uptimeMillis();
            mainHandler.postAtFrontOfQueue(this);
        }

        boolean isBlocked() {
    
            return !mCompleted || executeTime - mStartTime >= timeout;
        }
    }

    public interface ANRListener {
    
        void onAnrHappened(String stackTraceInfo);

        void onNotAnrHappened();
    }

    private ANRChecker anrChecker = new ANRChecker();

    private ANRListener anrListener;

    public ANRWatchDog addANRListener(ANRListener listener) {
    
        this.anrListener = listener;
        return this;
    }

    public static ANRWatchDog getInstance() {
    
        if (sWatchdog == null) {
    
            sWatchdog = new ANRWatchDog();
        }
        return sWatchdog;
    }

    private ANRWatchDog() {
    
        super("ANR-WatchDog-Thread");
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void run() {
    
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // 设置为后台线程
        while (true) {
    
            while (!isInterrupted()) {
    
                synchronized (this) {
    
                    anrChecker.schedule();
                    long waitTime = timeout;
                    long start = SystemClock.uptimeMillis();
                    while (waitTime > 0) {
    
                        try {
    
                            wait(waitTime);
                        } catch (InterruptedException e) {
    
                            Log.w(TAG, e.toString());
                        }
                        waitTime = timeout - (SystemClock.uptimeMillis() - start);
                    }
                    if (!anrChecker.isBlocked()) {
    
                        if (anrListener != null) {
    
                            //没有ANR发生
                            anrListener.onNotAnrHappened();
                        }
                        continue;
                    }
                }
                if (!ignoreDebugger && Debug.isDebuggerConnected()) {
    
                    continue;
                }
                String stackTraceInfo = getStackTraceInfo();
                if (anrListener != null) {
    
                    anrListener.onAnrHappened(stackTraceInfo);
                }
            }
            anrListener = null;
        }
    }

    private String getStackTraceInfo() {
    
        StringBuilder stringBuilder = new StringBuilder();
        for (StackTraceElement stackTraceElement : Looper.getMainLooper().getThread().getStackTrace()) {
    
            stringBuilder
                    .append(stackTraceElement.toString())
                    .append("\r\n");
        }
        return stringBuilder.toString();
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Android_xiong_st/article/details/123750861

智能推荐

华为C/C++笔试题_jychow的博客-程序员秘密

华为C/C++笔试题(附答案) 关键词:C语言面试题  C++面试题  华为面试题  本文地址:http://www.teecool.com/post/2007081104.html内容正文: 1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)int a = 4;(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D)

【tensorflow 错误解决】Could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILED_东华果汁哥的博客-程序员秘密_cudnn_status_alloc_failed

报错信息提示:E tensorflow/stream_executor/cuda/cuda_dnn.cc:329] Could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILED原因分析:GPU显存设置有问题,需要设置为仅在需要时申请显存。import tensorflow as tfconfig = tf.compat.v1.Conf...

手把手教你Laravel 集成 GatewayWorker (Workerman),实现简单的聊天系统._凡尘-追梦者的博客-程序员秘密

GatewayWorker 与 Workerman的关系Workerman可以看做是一个纯粹的socket类库,可以开发几乎所有的网络应用,不管是TCP的还是UDP的,长连接的还是短连接的。Workerman代码精简,功能强大,使用灵活,能够快速开发出各种网络应用。同时Workerman相比GatewayWorker也更底层,需要开发者有一定的多进程编程经验。因为绝大多数开发者的目标是基于Workerman开发TCP长连接应用,而长连接应用服务端有很多共同之处,例如它们有相同的进程模型以及单发、群发、广

Qt6.2.3使用vscode编写Qt项目_vscode 中编写qt_RiCoCoSoul的博客-程序员秘密

前言关于怎么下载安装Qt6.0和vscode这里就不讲了,网上的教程多如牛毛。再次说明此教程只适合有Qt基础和Cmake基础的人!!!测试环境Qt版本:6.2.3vscode:1.63.2编译器:MinGW64-GCC-11.2设置找到Qt安装目录找到路径目录*:\Qt\6.2.3\mingw_64(此路径包含了库文件)和*:\Qt\Tools\mingw900_64\bin(Qt自带的MinGw编译器)设置环境变量这里有两种方法一是直接使用Qt内置的工具提供的MinGW,或者.

启动Vue项目执行npm run serve报错npm ERR! [email protected] serve: `vue-cli-service serve --open`_会哭的孩子有奶喝的博客-程序员秘密

问题想启动一个Vue项目,在终端输入npm run serve后报错:解决方法参考 https://blog.csdn.net/CC1991_/article/details/108152325 ,发现很容易解决,只需要先把项目里面的node_modules文件夹直接删掉,再执行命令npm install,安装完毕之后,再次在终端里面输入命令行:npm run serve 回车,就不再报错。总结:启动Vue项目执行npm run serve报错首先删掉node_modules文件夹然后执行

随便推点

java 判断鼠标_JAVA 怎么判断鼠标单击处是否在某一图形上_女儿奴的RPG的博客-程序员秘密

初次接触对图形操作的问题,实在不会了,请教。问题:对通过多线程产生的各个球分别进行操作,判断再次点击处是否在图形上,这个判断整不了,下面是代码,由于长度限制的问题省略了部...初次接触对图形操作的问题,实在不会了,请教。问题:对通过多线程产生的各个球分别进行操作,判断再次点击处是否在图形上,这个判断整不了,下面是代码,由于长度限制的问题省略了部分系统自动添加代码。package mypaint;i...

并行与分布式计算 线程案例1_Jxufe渣渣斯的博客-程序员秘密_并行与分布式系统实验一

案例一使用读写锁实现线程同步读写锁与互斥量类似,但读写锁允许更高的并行性。其特性为:写独占,读共享。读写锁特性:(1)读写锁是“写模式加锁”时,解锁前,所有对该锁加锁的线程都会被阻塞。(2)读写锁是“读模式加锁”时,如果线程以读模式对其加锁会成功。如果线程以写模式加锁会阻塞。(3)读写锁是“读模式加锁”时,如果有另外线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求,这样可以避免...

iOS中assign,copy,retain之间的区别以及weak和strong的区别_luco2008的博客-程序员秘密

@property (nonatomic, assign) NSString *title; 什么是assign,copy,retain之间的区别? assign: 简单赋值,不更改索引计数(Reference Counting)。 copy: 建立一个索引计数为1的对象,然后释放旧对象 retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1 

如何删除无效的网络驱动器_大山老树的博客-程序员秘密_删除失效的网络驱动器

机器A共享了一个目录,机器B把这个共享目录映射到本地成为一个网络驱动器;后来机器A修改了密码,导致机器B访问网络驱动器出错;想删除这个链接,重新输密码,但在控制台通过net use /delete命令删除这个共享,总提示“引用账户当前已锁定,且可能无法登录”,怎么也删不了。最后没招了,把机器B注销了,再次登陆,无效的网络驱动器已消失,问题解决!

运用PARALLEL方式成倍提升Oracle数据分析效率_keueng的博客-程序员秘密_在oracle 的查询语句中使用parallel(8) 会提升查询速度吗

运用PARALLEL方式成倍提升Oracle数据分析效率Oracle作为一种大型数据库,在我国已成为大型企事业单位(如公立医院)的主流数据库并占有了绝对的市场份额。这就意味着审计工作同Oracle的交集越来越多,同时这种数据库的使用也意味着远超SQL SERVER的海量数据信息,其数据分析效率成为严重制约审计工作进度的瓶颈。本文将介绍在Oracle中如何通过采用PARALLEL(并行)方式最大化调用

设备连接:Ubuntu16.04 ROS中连接Hokuyo激光雷达UTM-30LX-EW_Upupup6的博客-程序员秘密

这次连接的设备如下:(以下是一步步的设备连接到采集数据的介绍,没法再详细了吧) -----第一步:连接硬件----首先雷达需要的供电电压是12V,PC需要的供电电压是24V。所以本次连接是两个电源。------第二步:通信-----之后就是在ROS下给实现PC与雷达的通信了。首先在Ubuntu下打开一个终端,为了防止下次下次查找,我是直接在Documents下建立...

推荐文章

热门文章

相关标签