Matrix卡顿优化之IdleHandlerLagTracer源码分析
前言
IdleHandler是Android系统为开发者提供的一种在消息队列空闲时运行任务的机制,通过IdleHandler执行的任务优先级低于主线程优先级,会在主线程任务执行完成后再执行,所以适用于一些实时性要求不高的任务,通常用于Android启动优化中,将一些优先级较低的任务延后执行,以提高应用启动速度。看下消息队列中的源码。
@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。
public IdleHandlerLagTracer(TraceConfig config) {traceConfig = config;
}
onStartTrace
onStartTrace会调用到onAlive方法,我们看onAlive的源码,首先初始化了一个HandlerThread,然后创建了一个IdleHandlerLagRunnable,最后调用了detectIdleHandler开启监控。
@Override
public void onAlive() {super.onAlive();if (traceConfig.isIdleHandlerTraceEnable()) {//异步线程idleHandlerLagHandlerThread = new HandlerThread("IdleHandlerLagThread");//上报信息用的runnableidleHandlerLagRunnable = new IdleHandlerLagRunable();detectIdleHandler();}
}
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<MessageQueue.IdleHandler> myIdleHandlerArrayList = new MyArrayList<>();//反射替换消息队列中的Listfield.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方法调用的目的。
static class MyArrayList<T> extends ArrayList {Map<MessageQueue.IdleHandler, MyIdleHandler> map = new HashMap<>();@Overridepublic 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);}@Overridepublic 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方法中,也就都在我们的监控之内了。
static class MyIdleHandler implements MessageQueue.IdleHandler {@Overridepublic 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
这里也就是在收集信息上报了。
static class IdleHandlerLagRunable implements Runnable {@Overridepublic 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方法,这里就是当任务停止时,移除所有消息。
@Override
public void onDead() {super.onDead();if (traceConfig.isIdleHandlerTraceEnable()) {idleHandlerLagHandler.removeCallbacksAndMessages(null);}
}
总结
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
相关文章:
Matrix卡顿优化之IdleHandlerLagTracer源码分析
前言 IdleHandler是Android系统为开发者提供的一种在消息队列空闲时运行任务的机制,通过IdleHandler执行的任务优先级低于主线程优先级,会在主线程任务执行完成后再执行,所以适用于一些实时性要求不高的任务,通常用于Android启动…...
(ubuntu)Docker 安装linux 详情过程
文章目录 前言Docker 安装linux第一步:使用dokcker 拉取镜像:第二步:创建本地目录(用于挂载)第三步:(上传配置文件)修改配置文件第四步:创建docker容器第五步: 测试本地连…...
ArcMap:第二届全国大学生GIS技能大赛(广西师范学院)详解-上午题
目录 01 题目 1.1 第一小题 1.2 第二小题 1.3 第三小题 1.4 数据展示 02 思路和实操 2.1 第一问思路 2.2 第一问操作过程 2.2.1 地理配准 2.2.2 镶嵌 2.2.2.1 第一种镶嵌方法 2.2.2.2 第二种镶嵌方法 2.2.3 裁剪 2.2.4 DEM信息提取 2.2.5 分类 2.3 第二问思路 …...
Blender 导出 fbx 到虚幻引擎中丢失材质!!!(使用Blender导出内嵌材质的fbx即可解决)
目录 0 引言1 Blender导出内嵌纹理的fbx模型 0 引言 我在Blender处理了一些fbx模型后再次导出到UE中就经常出现,材质空白的情况(如下图所示),今天终于找到问题原因,记录下来,让大家避免踩坑。 其实原因很简…...
C++交换a和b的方法
以下是用C编写的交换a和b的六种方法: 1. 方法一:使用临时变量 #include <iostream>int main() {int a 5;int b 10;std::cout << "Before swapping: a " << a << ", b " << b << std::end…...
3D孪生场景搭建:模拟仿真
前面几期文章介绍如何使用NSDT 编辑器 搭建3D应用场景,本期介绍下孪生场景中一个一个非常重要的功能:模拟仿真。 1、什么是模拟仿真 模拟仿真是一种用于描述、分析和模拟现实世界中系统、过程或事件的计算机模型和程序。仿真通过输入各种参数和条件&am…...
美国各流域边界下载,并利用arcgis提取与处理
一、边界数据的下载 一般使用最普遍的流域边界数据是从HydroSHEDS官网下载: HydroBASINS代表一系列矢量多边形图层,以全球尺度呈现次级流域边界。该产品的目标是提供一种无缝的全球覆盖,其中包含了不同尺度(从数十到数百万平方千米…...
A Survey and Framework of Cooperative Perception 论文阅读
论文链接 A Survey and Framework of Cooperative Perception: From Heterogeneous Singleton to Hierarchical Cooperation 0. Abstract 首次提出统一的 CP(Cooperative Percepetion) 框架回顾了基于不同类型传感器的 CP 系统与分类对节点结构&#x…...
【SkyWalking】SkyWalking是如何实现跨进程传播链路数据?
文章目录 一、简介1 为什么写这篇文章2 跨进程传播协议-简介 二、协议1 Standard Header项2 Extension Header项3 Correlation Header项 三、跨进程传播协议的源码分析1 OpenTracing规范2 通过dubbo插件分析跨进程数据传播3 分析跨进程传播协议的核心源码 四、小结参考 一、简介…...
px4仿真实现无人机自主飞行
一,确定消息类型 无人机通过即在电脑是现自主飞行:思路如下。 通过Mavros功能包,将ROS消息转换为Mavlink消息。实现对无人机的控制。 几种消息之间的关系如下: 对于ROS数据,就是我们机载电脑执行ROS系统的数据。 对于Mavros消息,就是Mavros功能包内部的消息。查询网站…...
详解Linux的系统调用fork()函数
在Linux系统中,fork()是一个非常重要的系统调用,它的作用是创建一个新的进程。具体来说,fork()函数会在当前进程的地址空间中复制一份子进程,并且这个子进程几乎完全与父进程相同,包括进程代码、数据、堆栈以及打开的文…...
构建捡垃圾机器人的 ROS 2 项目
一、说明 本系列是关于学习如何使用 ROS2、Docker 和 Github 设计、设置和维护机器人项目。 先决条件 — ROS2 软件包的基本知识、实现发布者、订阅者、操作并连接它们。 我们之前在 ROS2 中了解了不同的部分。但是,在我们转向实际的基于硬件的项目之前,…...
Spring常用注解(2)
6、切面(AOP)相关注解 Spring AOP详细介绍 Spring支持AspectJ的注解式切面编程。 Aspect 声明一个切面 After 在方法执行之后执行(方法上) Before 在方法执行之前执行(方法上) Around 在方法执行之前与之后…...
upload-labs靶场通关
文章目录 Pass-01 前端检测(JS检测)1.1 原理分析1.2 实验 Pass-02 后端检测(MIME检测)2.1 原理分析2.2 实验 Pass-03 后端检测(黑名单绕过,特殊后缀名)3.1 原理分析3.2 实验 Pass-04 后端检测&a…...
git拉取代码过程
第一步:先在本地创建文件夹 ,比如我这里的文件夹名称是 fengkgong_zntjfx 第二步:执行命令:git init 第三步:git clone 第四步:git fetch 第五步:git branch -a 第六步:cd 项目 【…...
Swift | 属性包装器
Swift | 属性包装器 1. 什么是 Swift Property Wrapper? Swift Property Wrapper 是一种特性,它允许我们在声明属性时添加自定义的包装逻辑。通过使用属性包装器,我们可以在不修改类或结构体定义的情况下,定制属性的访问和存储方…...
Android改造CardView为圆形View,Kotlin
Android改造CardView为圆形View,Kotlin 可以利用androidx.cardview.widget.CardView的cardCornerRadius特性,将CardView改造成一个圆形的View,技术实现的关键首先设定CardView为一个宽高相等的View(正方形),…...
Idea下面git的使用:变基、合并、优选、还原提交、重置、回滚、补丁
多分支和分支切换 变基和合并 变基是把本项目的所有提交都列出来按顺序一个个提交到目标分支上去 而合并是把两个分支合并起来,但是旧的分支还是可以启动其他分支,在旧的分支上继续开发 master: A -- B -- C -- M/ feature: D -- Emaster: A -…...
【数据结构】什么是算法
🦄个人主页:修修修也 🎏所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.算法的定义 1.算法的概念 2.数据结构与算法的关系 二.算法的特性 输入 输出 有穷性 确定性 可行性 三.算法的设计要求 1.正确性 2.可读性 3.健壮性 4.效…...
复旦大学EMBA:揭秘科创企业,领略未来战略!
智能制造,国之重器。作为制造强国建设的主攻方向,智能制造的发展水平关系到我国未来制造业在全球的地位与影响力。发展智能制造,是加快建设现代化产业体系的重要手段,提升供给体系适配性的有力抓手,也是建设数字中国的…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
