Android性能优化系列-腾讯matrix卡顿优化之IdleHandlerLagTracer源码分析

这是性能优化系列之matrix框架的第13篇文章,我将在性能优化专栏中对matrix apm框架做一个全面的代码分析,性能优化是Android高级工程师必知必会的点,也是面试过程中的高频题目,对性能优化感兴趣的小伙伴可以去我主页查看所有关于matrix的分享。

前言

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

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

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

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

  • 构造方法
  • onStartTrace
  • onStopTrace

构造方法

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

arduino 复制代码
public IdleHandlerLagTracer(TraceConfig config) {
    traceConfig = config;
}

onStartTrace

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

scss 复制代码
@Override
public void onAlive() {
    super.onAlive();
    if (traceConfig.isIdleHandlerTraceEnable()) {
        //异步线程
        idleHandlerLagHandlerThread = new HandlerThread("IdleHandlerLagThread");
        //上报信息用的runnable
        idleHandlerLagRunnable = new IdleHandlerLagRunable();
        detectIdleHandler();
    }
}

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

ini 复制代码
private static void detectIdleHandler() {
    MessageQueue mainQueue = Looper.getMainLooper().getQueue();
    Field field = MessageQueue.class.getDeclaredField("mIdleHandlers");
    field.setAccessible(true);
    MyArrayList<MessageQueue.IdleHandler> myIdleHandlerArrayList = new MyArrayList<>();
    //反射替换消息队列中的List
    field.set(mainQueue, myIdleHandlerArrayList);
    idleHandlerLagHandlerThread.start();
    idleHandlerLagHandler = new Handler(idleHandlerLagHandlerThread.getLooper());
}

MyArrayList

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

typescript 复制代码
static class MyArrayList<T> extends ArrayList {
    Map<MessageQueue.IdleHandler, MyIdleHandler> 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);
       }
   }
}

MyIdleHandler

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

typescript 复制代码
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;
    }
}

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

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

IdleHandlerLagRunnable

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

ini 复制代码
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);
    }
}

onStopTrace

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

typescript 复制代码
@Override
public void onDead() {
    super.onDead();
    if (traceConfig.isIdleHandlerTraceEnable()) {
        idleHandlerLagHandler.removeCallbacksAndMessages(null);
    }
}

总结

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

相关推荐
数据猎手小k3 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小104 小时前
JavaWeb项目-----博客系统
android
风和先行4 小时前
adb 命令查看设备存储占用情况
android·adb
无尽的大道5 小时前
Java反射原理及其性能优化
jvm·性能优化
AaVictory.5 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
测试19986 小时前
2024软件测试面试热点问题
自动化测试·软件测试·python·测试工具·面试·职场和发展·压力测试
似霰6 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶6 小时前
Android——网络请求
android
干一行,爱一行6 小时前
android camera data -> surface 显示
android
断墨先生6 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app