• Matrix卡顿优化之IdleHandlerLagTracer源码分析


    前言

    IdleHandler是Android系统为开发者提供的一种在消息队列空闲时运行任务的机制,通过IdleHandler执行的任务优先级低于主线程优先级,会在主线程任务执行完成后再执行,所以适用于一些实时性要求不高的任务,通常用于Android启动优化中,将一些优先级较低的任务延后执行,以提高应用启动速度。看下消息队列中的源码。

    @UnsupportedAppUsage
    Message next() {
        //前边省略了很多代码,只有消息队列当前没有需要执行的任务时,才会执行到下边的代码
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            keep = idler.queueIdle();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    那么既然IdleHandler是用于进行性能优化的,为什么matrix还要对其进行监控呢?从上边的分析我们可以知道,IdleHandler也是在主线程消息队列中运行的,所以假如IdleHandler中出现了耗时任务执行,那么很明显就会导致主线程卡顿,IdleHandler也是属于主线程卡顿监控的关键一环。

    了解了IdleHandler监控的必要性,我们现在开始今天的源码分析。和其他类型的tracer一致,IdleHandler也是在TracePlugin中进行初始化和调用的,那么我们就从这几个关键方法入手:

    • 构造方法
    • onStartTrace
    • onStopTrace

    构造方法

    构造方法仅仅是拿到了传入的配置,配置中包含的是IdleHandler监控是否启用的开关,isIdleHandlerTraceEnable。

    public IdleHandlerLagTracer(TraceConfig config) {
        traceConfig = config;
    }
    
    • 1
    • 2
    • 3

    onStartTrace

    onStartTrace会调用到onAlive方法,我们看onAlive的源码,首先初始化了一个HandlerThread,然后创建了一个IdleHandlerLagRunnable,最后调用了detectIdleHandler开启监控。

    @Override
    public void onAlive() {
        super.onAlive();
        if (traceConfig.isIdleHandlerTraceEnable()) {
            //异步线程
            idleHandlerLagHandlerThread = new HandlerThread("IdleHandlerLagThread");
            //上报信息用的runnable
            idleHandlerLagRunnable = new IdleHandlerLagRunable();
            detectIdleHandler();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    IdleHandlerLagRunnable是用于上报信息的,我们先看detectIdleHandler方法。首先拿到主线程消息队列对象,然后通过反射从MessageQueue对象上获取到mIdleHandlers的Field对象,mIdleHandlers是一个List集合,内部存储了所有当前消息队列添加的IdleHandler对象。拿到之后构造了一个自定义的List-MyArrayList,反射将其设置到消息队列上,这里的目的是将mIdleHandlers作为一个hook点,完成替换之后,主线程添加和移除IdleHandler的操作都在我们的监控范围之内了。

    private static void detectIdleHandler() {
        MessageQueue mainQueue = Looper.getMainLooper().getQueue();
        Field field = MessageQueue.class.getDeclaredField("mIdleHandlers");
        field.setAccessible(true);
        MyArrayList myIdleHandlerArrayList = new MyArrayList<>();
        //反射替换消息队列中的List
        field.set(mainQueue, myIdleHandlerArrayList);
        idleHandlerLagHandlerThread.start();
        idleHandlerLagHandler = new Handler(idleHandlerLagHandlerThread.getLooper());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    MyArrayList

    看下MyArrayList的实现。它继承自ArrayList,重写了add和remove方法,也就是拦截了IdleHandler的添加和移除。当通过调用MessageQueue的addIdleHandler方法向list中添加时,就会走到MyArrayList的add方法中,此时会将IdleHandler再包装一层MyIdleHandler存入,达到拦截IdleHandler的queueIdle方法调用的目的。

    static class MyArrayList extends ArrayList {
        Map map = new HashMap<>();
        @Override
        public boolean add(Object o) {
            if (o instanceof MessageQueue.IdleHandler) {
                //包装一层,作为代理。拦截queueIdle方法的执行
                MyIdleHandler myIdleHandler = new MyIdleHandler((MessageQueue.IdleHandler) o);
                //记录映射关系
                map.put((MessageQueue.IdleHandler) o, myIdleHandler);
                return super.add(myIdleHandler);
            }
            return super.add(o);
        }
        @Override
        public boolean remove(@Nullable Object o) {
           if (o instanceof MyIdleHandler) {
               MessageQueue.IdleHandler idleHandler = ((MyIdleHandler) o).idleHandler;
               map.remove(idleHandler);
               return super.remove(o);
           } else {
               MyIdleHandler myIdleHandler = map.remove(o);
               if (myIdleHandler != null) {
                  return super.remove(myIdleHandler);
               }
               return super.remove(o);
           }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    MyIdleHandler

    接下来我们看看MyIdleHandler是怎么实现的。可以看到它继承自IdleHandler,并重写了它的queueIdle方法,这样一来每一个IdleHandler执行时都会走到MyIdleHandler的queueIdle方法中,也就都在我们的监控之内了。

    static class MyIdleHandler implements MessageQueue.IdleHandler {
        @Override
        public boolean queueIdle() {
            //发送延时消息,延时内未执行完成就上报
            idleHandlerLagHandler.postDelayed(idleHandlerLagRunnable, traceConfig.idleHandlerLagThreshold);
            boolean ret = this.idleHandler.queueIdle();
            //执行完成则移除延时消息
            idleHandlerLagHandler.removeCallbacks(idleHandlerLagRunnable);
            return ret;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    当queueIdle执行的时候,通过idleHandlerLagHandler发送一个延时2s(默认)的消息,idleHandlerLagHandler是一个和HandlerThread绑定的Handler,它会将消息发送到HandlerThread子线程执行,假如2s内queueIdle方法执行完成,那么这个消息就会被移除,也就是不会触发上报。

    这个消息做了什么呢?我们接下来看看这个idleHandlerLagRunnable。

    IdleHandlerLagRunnable

    这里也就是在收集信息上报了。

    static class IdleHandlerLagRunable implements Runnable {
        @Override
        public void run() {
            String stackTrace = Utils.getMainThreadJavaStackTrace();
            boolean currentForeground = AppForegroundUtil.isInterestingToUser();
            String scene = AppActiveMatrixDelegate.INSTANCE.getVisibleScene();
            JSONObject jsonObject = new JSONObject();
            jsonObject = DeviceUtil.getDeviceInfo(jsonObject, Matrix.with().getApplication());
            jsonObject.put(SharePluginInfo.ISSUE_STACK_TYPE, Constants.Type.LAG_IDLE_HANDLER);             jsonObject.put(SharePluginInfo.ISSUE_SCENE, scene);
            jsonObject.put(SharePluginInfo.ISSUE_THREAD_STACK, stackTrace);
            jsonObject.put(SharePluginInfo.ISSUE_PROCESS_FOREGROUND, currentForeground);
            Issue issue = new Issue();
            issue.setTag(SharePluginInfo.TAG_PLUGIN_EVIL_METHOD);
            issue.setContent(jsonObject);
            plugin.onDetectIssue(issue);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    onStopTrace

    onStopTrace会调用到onDead方法,这里就是当任务停止时,移除所有消息。

    @Override
    public void onDead() {
        super.onDead();
        if (traceConfig.isIdleHandlerTraceEnable()) {
            idleHandlerLagHandler.removeCallbacksAndMessages(null);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    总结

    IdleHandlerLagTracer的实现逻辑还是很简单的,它通过hook的方法替换了主线程消息队列的IdleHandlers集合,从而拦截到了IdleHandler的添加和移除逻辑,在拦截到添加IdleHandler的操作时,为原来的IdleHandler做一层代理,从来可以在queueIdle方法执行时做超时监听逻辑,超时未执行完成则收集信息上报,从而发现IdleHandler导致的卡顿问题。

    Android 学习笔录

    Android 性能优化篇:https://qr18.cn/FVlo89
    Android Framework底层原理篇:https://qr18.cn/AQpN4J
    Android 车载篇:https://qr18.cn/F05ZCM
    Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
    Android 音视频篇:https://qr18.cn/Ei3VPD
    Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
    OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
    Kotlin 篇:https://qr18.cn/CdjtAF
    Gradle 篇:https://qr18.cn/DzrmMB
    Flutter 篇:https://qr18.cn/DIvKma
    Android 八大知识体:https://qr18.cn/CyxarU
    Android 核心笔记:https://qr21.cn/CaZQLo
    Android 往年面试题锦:https://qr18.cn/CKV8OZ
    2023年最新Android 面试题集:https://qr18.cn/CgxrRy
    Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
    音视频面试题锦:https://qr18.cn/AcV6Ap

  • 相关阅读:
    【算法模板】图论:最近公共祖先(LCA)
    字节小程序交易组件使用指南
    1079 Total Sales of Supply Chain
    “医药分离”大背景下,连锁药店如何加速扩张
    sql note 链接查询
    号码认证平台有哪些?号码认证平台费用?
    自动化测试-鉴权
    【spring】SpringBoot与SpringCloud的版本对应
    为什么要让员工入职流程实现自动化
    A40I工控主板(SBC-X40I)T卡接口,EMMC读写测试
  • 原文地址:https://blog.csdn.net/maniuT/article/details/133639168