初衷
我的想法很简单,就是想知道每一个自定义方法执行时间,最好有直观的图表,而不用从大量的Profiler文件中抽丝剥茧。
阶段性成果
在开发中逐步完善,现在具备了4大基本功能
- 统计指定包下面的函数执行耗时
- 统计方法执行前后的内存数据
- 标记执行时间超过5000ms 或者 ANR 函数
- 统计电量变化
不知道有不有人感兴趣,先把源码放这里
源码:https://github.com/woshiwzy/APM_MT
效果图
整体功能
过程感悟
从下决心做这件事到现在,前后差不多一个多月,整个过程也学会了不少东西,充分的体会到
纸上得来终觉浅,绝知此事要躬行
略有所获
- Gradle 插件开发流程
- ASM字节码插装与代码生成技术
- Jvmti 技术(很有用,但没用到这个项目中)
- Python echart 绘图技术
- Android Framework知识
- Nexus私有仓库搭建知识
- 私有插件与私有库托管技术
- 其他
开发过程中遇到的最大的坑
Gradle的自带的asm相关的类混乱,导致ASM代码无法通过编译,最后下载了asm9.2 和 common-io-2.6 彻底解决。
可扩展
自定义实现
假如你想自定义收集处理机制,可以随意修改com.myapm.callback.MTCallBack的mtStart 和mtDone的具体实现。默认收集到的日志格式如下
MT_D,main,com.sand.apm.mt.App.onCreate,1652059891656,6.0MB,12.0MB,90,61,6.0MB,12.0MB,90
以逗号隔开信息分别如下
- MT_D:代表收集到了结束信息,但是方法任然可能超过5秒甚至更多,MT_N:代表没收集到结束信息,cost会被设置成9999 方便显示
- main:线程名
- com.sand.apm.mt.App.onCreate:执行方法
- 1652059891656:开始执行的时候时间戳
- 6.0MB:开始执行时Java堆
- 12.0MB:开始执行时的Native堆
- 90:方法执行前电量
- 61:方法耗时毫秒数
- 6.0MB:方法执行后的java堆
- 12.0MB:方法执行后的Naative堆
- 90:方法执行后的电量
手动调用
你可以在任意方法开始处调用
long currentTimeMillis = System.currentTimeMillis();
MTCallBack.mtStart(currentTimeMillis);
方法结束处调用
MTCallBack.mtDone(currentTimeMillis);
MT自带的机制也会给你记录下来并最终写到日志文件中
MT插件工作原理
第一步:插桩
在编译的过程中对指定路径下的类文件进行插装,例如
public static void method1(){
System.out.println("我是method1");
}
插装后
public static void method1(){
long currentTimeMillis = System.currentTimeMillis();
MTCallBack.mtStart(currentTimeMillis);
System.out.println("我是method1");
MTCallBack.mtDone(currentTimeMillis);
}
第二步:运行时调用
其中MTCallBack是根据插件配置参数自动生成的类
MTCallBack.mtStart 和 MTCallBack.mtDone
是自动生成的静态方法,通过静态方法调用收集运行时的信息,比如方法执行时间,运行前后的内存信息,电量等。
主要的收集工作在com.sand.apm.mtlib.Statistics类中完成。
第三步:输出日志
Statistics 类中存在一个线程池,每个10秒钟把收集到日志写入到磁盘中,默认的路径是
/sdcard/Android/data/包名/files/Download/mtfiles
第四步:使用Python脚本生成渲染图
环境 python3,pyechats v1
自动化步骤
- 通过adb pull 把日志拷贝到电脑指定的目录
- 通过python脚本读取解析日志,生成可视图图表
整体效果图(所有图标带拖拽和缩放,手感比profiler 更佳)
统计出所有指定包下的方法执行时间
单独统计出执行时间超过5000ms的函数
统计方法运行前后的Java和Native堆
统计出方法运行前后的电量