美文网首页
监控主线程耗时操作,从开发中解决ANR

监控主线程耗时操作,从开发中解决ANR

作者: super可乐 | 来源:发表于2022-09-17 20:02 被阅读0次

    背景:

    debug环境中主线程中方法体执行的时间与指定的时间做对比后的堆栈信息,针对性的优化超过指定时间的耗时方法体,减少ANR的发生

    此工具类主要是通过向主线程Looper打印超过指定时间的耗时堆栈信息以及耗时时长,其中校验时间自已定义,主动查看主线程中的耗时操作,防患未然。

    原理:

    此工具类为最简单最直接处理、优化耗时操作的工具 大家都知道Android 对于ANR的判断标准:

    最简单的一句话就是:ANR——应用无响应,Activity是5秒,BroadCastReceiver是10秒,Service是20秒

    然后此工具类的方案就是将主线程的堆栈信息作时间对比监控,超时的打印出来

    Looper.loop 解析:

    1. 应用之所以未退出,就是运行在loop 中,如果有阻塞loop 的操作就会发生ANR、崩溃
    public static void loop() {
        final Looper me = myLooper();
        //....
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }
    
    1. 主要看死循环 loopOnce
    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }
    
        // This must be in a local variable, in case a UI event sets the logger
        // *当有任务的时候打印Dispatching to *
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what);
        }
        //.... 中间部分未任务执行的代码
    
        //执行结束之后打印 Finished to 
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
    
        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }
    
        msg.recycleUnchecked();
    
        return true;
    }
    
    1. 上述注释之间的耗时就是主线程在执行某个任务时的耗时,我们只要拿这个时间和指定时间相比就能监控主线程的耗时堆栈信息了

    使用方式:

    1. Application:
     //主线程中方法体执行的时间与指定的时间做对比后的堆栈信息,针对性的优化超过指定时间的耗时方法体,
    MainThreadDoctor.init(500)
    
    1. 查看日志:

    日志等级为明显起见使用error级别

    工具类:

     /**
    *  @author  kong
    *  @date  2022/7/6 15:55
    *  @description  在debug环境中主线程中方法体执行的时间与指定的时间做对比后的堆栈信息,针对性的优化超过指定时间的耗时方法体,减少ANR的发生
    **/
    object MainThreadDoctor {
    
        private  var startTime = 0L
        private  var currentJob: Job? = null
     private  const  val START = ">>>>> Dispatching"
        private  const  val END = "<<<<< Finished"
    
        fun init(diagnoseStandardTime: Long) {
            if (BuildConfigs.DEBUG) {
                diagnoseFromMainThread(diagnoseStandardTime)
            }
        }
    
        /**
    *  @param diagnoseStandardTime 执行诊断的标准时间
    */
     fun diagnoseFromMainThread(diagnoseStandardTime: Long) {
            Looper.getMainLooper().setMessageLogging {
     if (it.startsWith(START)) {
                    startTime = System.currentTimeMillis()
                    currentJob = GlobalScope.launch(Dispatchers.IO) {
    delay(diagnoseStandardTime)
                        val stackTrace = Looper.getMainLooper().thread.stackTrace
     val builder = StringBuilder()
                        for (s in stackTrace) {
                            builder.append(s.toString())
                            builder.append("\n")
                        }
                        PPLog.e("looperMessageMain $builder")
                    }
    }
    
                if (it.startsWith(END)) {
                    if (currentJob?.isCompleted == false) {
                        currentJob?.cancel()
                    } else {
                        PPLog.e("looperMessageMain 总时间 = ${System.currentTimeMillis() - startTime} 毫秒")
                    }
                }
            }
    }
    }
    

    作者:狼窝山下的青年
    链接:https://juejin.cn/post/7117194640826368036

    相关文章

      网友评论

          本文标题:监控主线程耗时操作,从开发中解决ANR

          本文链接:https://www.haomeiwen.com/subject/luyeortx.html