文章目录
- 一、官方地址
- 二、Demo分析
- 三、整体结构
- 四、matrix-android-lib
- 五、ResourceCanary内存泄露检测
- 六、IOCanaryPlugin
- 七、matrix-trace-canary
- 八、matrix-sqlite-lint
- 九、APK Checker
- 未完待续....
Matrix 是一款微信研发并日常使用的 APM(Application Performance Manage),当前主要运行在 Android 平台上
一、官方地址
- Tencent/matrix
- Matrix 输出内容的含义解析
- Matrix ResourceCanary
- Matrix ApkChecker
- Matrix SQLiteLint
- Matrix-常见问题
二、Demo分析
只看核心代码即可,关于相关变量我们后文解释:
Matrix.Builder builder = new Matrix.Builder(application); // build matrix
builder.patchListener(new TestPluginListener(this)); // add general pluginListener
DynamicConfigImplDemo dynamicConfig = new DynamicConfigImplDemo(); // dynamic config
// init plugin
IOCanaryPlugin ioCanaryPlugin = new IOCanaryPlugin(new IOConfig.Builder()
.dynamicConfig(dynamicConfig)
.build());
//add to matrix
builder.plugin(ioCanaryPlugin);
//init matrix
Matrix.init(builder.build());
// start plugin
ioCanaryPlugin.start();
三、整体结构
- matrix-android-lib
- matrix-gradle-plugin
- matrix-arscutil
- matrix-commons
- matrix-apk-canary
- matrix-resource-canary
- matrix-trace-canary
- matrix-sqlite-lint
- matrix-io-canary
- matrix-android-commons
说实话,如果大家有看过另外一个APM开源项目Kyson/AndroidGodEye,会很容易把握到整体的设计思路,这里有一篇针对AndroidGodEye的分析:(4.2.46)AndroidGodEye源码整体结构分析
建议在把握整体框架结构的基础上,进行进一步的细节了解。
按照 matrix 现在的描述,它提供的运行时监控能力:
- Resource Canary:
- Activity 泄漏
- Bitmap 冗余
- Trace Canary
- 界面流畅性
- 启动耗时
- 页面切换耗时
- 慢函数
- 卡顿
- SQLite Lint: 按官方最佳实践自动化检测 SQLite 语句的使用质量
- IO Canary: 检测文件 IO 问题
- 文件 IO 监控
- Closeable Leak 监控
四、matrix-android-lib
- Plugin 被定义为某种监控能力的抽象
- Issue 发生的某种被监控事件
- Report 一种观察者模式的实现,用于发现Issue时,通知观察者
- Matrix 单例模式的实现,暴漏给外部的接口
- matrix-config.xml 相关监控的配置项
- IDynamicConfig 开放给用户自定义的相关监控的配置项,其实例会被各个Plugin持有
4.1 Plugin监控能力
- IPlugin 一种监控能力的抽象
- PluginListener 开放给用户的,感知监控模块初始化、启动、停止、发现问题等生命周期的能力。 用户可自行实现,并注入到Matrix中
- Plugin
- 对IPlugin的默认实现,以触发相关PluginListener生命周期
- 这也就意味著:监控能力生命周期的触发,是在监控能力具体执行动作之前出发的
- 实现 IssuePublisher.OnIssueDetectListener接口的onDetectIssue方法
- 该方法会在 具体监控事件发生时,被
Matrix.with().getPluginByClass(xxx).onDetectIssue(Issue)
这样调用。注意这里是第一种通知方式 - 该方法内部对Issue相关环境变量进行赋值,譬如引发该Issue的Plugin信息
- 该方法最终触发
PluginListener#onReportIssue
- 该方法会在 具体监控事件发生时,被
- 对IPlugin的默认实现,以触发相关PluginListener生命周期
public interface IPlugin {
/**
* 用于标识当前的监控,相当于名称索引(也可用classname直接索引)
*/
String getTag();
/**
* 在Matrix对象构建时被调用
*/
void init(Application application, PluginListener pluginListener);
/**
* 对activity前后台转换的感知能力
*/
void onForeground(boolean isForeground);
void start();
void stop();
void destroy();
}
public interface PluginListener {
void onInit(Plugin plugin);
void onStart(Plugin plugin);
void onStop(Plugin plugin);
void onDestroy(Plugin plugin);
void onReportIssue(Issue issue);
}
4.2 IssuePublisher被监控事件观察者
- Issue 被监控事件
- type:类型,用于区分同一个tag不同类型的上报
- tag: 该上报对应的tag
- stack:该上报对应的堆栈
- process:该上报对应的进程名
- time:issue 发生的时间
- IssuePublisher 观察者模式
- 持有一个 发布Listener(其实现往往是上文的 Plugin)
- 持有一个 已发布信息的Map,在一次运行时长内,避免针对同一事件的重复发布
- 一般而言,某种监控的监控探测器往往继承该类,并在检测到事件发生时,调用
publishIssue(Issue)
—>IssuePublisher.OnIssueDetectListener接口的onDetectIssue方法—>**最终触发PluginListener#onReportIssue
- 注意这里是第二种通知方式
- FilePublisher 本地会记录已发布过的事件,在一次安装时长内,避免针对同一事件的重复发布
4.3 Utils 辅助能力
- MatrixLog 开放给用户的log能力
- MatrixHandlerThread 公共线程,往往用于异步线程执行一些任务
- DeviceUtil 获取相关设备信息
- MatrixUtil 判断主线程等其他utils
4.4 Matrix对外接口
public class Matrix {
private static final String TAG = "Matrix.Matrix";
/********************************** 单例实现 **********************/
private static volatile Matrix sInstance;
public static Matrix init(Matrix matrix) {
if (matrix == null) {
throw new RuntimeException("Matrix init, Matrix should not be null.");
}
synchronized (Matrix.class) {
if (sInstance == null) {
sInstance = matrix;
} else {
MatrixLog.e(TAG, "Matrix instance is already set. this invoking will be ignored");
}
}
return sInstance;
}
public static boolean isInstalled() {
return sInstance != null;
}
public static Matrix with() {
if (sInstance == null) {
throw new RuntimeException("you must init Matrix sdk first");
}
return sInstance;
}
/**************************** 构造函数 **********************/
private final Application application;
private final HashSet<Plugin> plugins;
private final PluginListener pluginListener;
private Matrix(Application app, PluginListener listener, HashSet<Plugin> plugins) {
this.application = app;
this.pluginListener = listener;
this.plugins = plugins;
for (Plugin plugin : plugins) {
plugin.init(application, pluginListener);
pluginListener.onInit(plugin);
}
}
/**************************** 控制能力 **********************/
public void startAllPlugins() {
for (Plugin plugin : plugins) {
plugin.start();
}
}
public void stopAllPlugins() {
for (Plugin plugin : plugins) {
plugin.stop();
}
}
public void destroyAllPlugins() {
for (Plugin plugin : plugins) {
plugin.destroy();
}
}
/**************************** get | set **********************/
public Plugin getPluginByTag(String tag) {
for (Plugin plugin : plugins) {
if (plugin.getTag().equals(tag)) {
return plugin;
}
}
return null;
}
public <T extends Plugin> T getPluginByClass(Class<T> pluginClass) {
String className = pluginClass.getName();
for (Plugin plugin : plugins) {
if (plugin.getClass().getName().equals(className)) {
return (T) plugin;
}
}
return null;
}
/**************************** 其他 **********************/
public static void setLogIml(MatrixLog.MatrixLogImp imp) {
MatrixLog.setMatrixLogImp(imp);
}
}
五、ResourceCanary内存泄露检测
- matrix-resource-canary-analyzer 被分离出来的,泄露路径检测工具和bimap冗余分析
- matrix-resource-canary-android 内存泄露检测工具
- matrix-resource-canary-common 一些基础utils
我们还是先看下官方给出来的说明文档
阅读上文后,我们可以得知:Matrix 的内存泄露检测基本是源自于 LeakCanary的,(如果对这个开源项目不太了解的建议去看下源码)
我们根据 流程概述:(4.2.39)内存泄漏检测LeakCanary源码分析, 简单做个回顾:
- Leakcanary通过Application.registerActivityLifecycleCallbacks()方法注册一个回调对象,在所有Activity的onActivityDestroyed()生命周期方法方法中,异步启动一次watch检测
- 对Activity是否泄漏的判断依赖VM会将可回收的对象加入WeakReference关联的ReferenceQueue
- 通过创建一个持有已销毁的Activity的WeakReference,然后主动触发一次GC,如果这个Activity能被回收,则持有它的WeakReference会被置空,且这个被回收的Activity一段时间后会被加入事先注册到WeakReference的一个队列里。这样我们就能间接知道某个被销毁的Activity能否被回收了。
- 如果观察到gc之后,目标对象未出现在ReferenceQueue,则认为可能发生了一次泄露
- 触发HeapDump.Listener
- 先使用HeapDumper进行 堆内存导出
2.开启一个跨进程后台服务HeapAnalyzerService,该后台服务启动后,调用【leakcanary-analyzer】对堆内存信息进行分析 - 分析结果通过发广播方式传给另外一个后台服务AbstractAnalysisResultService
- 先使用HeapDumper进行 堆内存导出
涉及到的对象如下:
- Leakcanary-watcher 实现了基本的内存检测框架,只是实现了监视机制,具体怎么监视+如何处理等都通过callback回调处理
- RefWatcher 核心观察器,持有以下实例对象以实现内存监视机制
- RefWatcherBuilder 构建[核心观察器],传入1-6号对象
- Set retainedKeys; //[核心]持有那些待检测以及产生内存泄露的引用的key
- ReferenceQueue queue; //[核心] 用于判断弱引用所持有的对象是否已被GC
- DebuggerControl debuggerControl; //[1.debug校验器] 用于查询是否正在调试中,调试中不会执行内存泄露检测
- GcTrigger gcTrigger; //[2.GC控制器] 用于在判断内存泄露之前,再给一次GC的机会
- HeapDumper heapDumper; //[3. 堆信息导出器] 用于在产生内存泄露室执行dump 内存heap
- WatchExecutor watchExecutor; //[4. 可延迟执行线城池] 执行内存泄露检测的线程池executor
- HeapDump.Listener heapdumpListener;//[5. 堆信息分析器] 用于分析前面产生的dump文件,找到内存泄露的原因
- ExcludedRefs excludedRefs; //[6. 白名单map]用于排除某些系统bug导致的内存泄露
- leakcanary-analyzer 提供了基于haha的堆内存分析器HeapAnalyzer,用来对 dump 出来的内存进行分析并返回内存分析结果 AnalysisResult,内部包含了泄漏发生的路径等信息供开发者寻找定位
- leakcanary-android: 这个 module 是与 Android 世界的接入点,用来专门监测 Activity 的泄漏情况,利用 leakcanary-watcher 来进行弱引用+手动 GC 机制进行监控
- RefWatcherBuilderAndroid 构建android使用的[核心观察器],传入1-6号android对象
- DisplayLeakService 后台服务[AbstractAnalysisResultService]的子类,其onHeapAnalyzed(…) 在泄漏时会被回调
- DebuggerControlAndroid //[1.debug校验器] 用于查询是否正在调试中,调试中不会执行内存泄露检测
- HeapDumperAndroid //[3. 堆信息导出器] 用于在产生内存泄露室执行dump 内存heap
- package dumpfilehelp //[3.1 IO文件目录管理器] 用于创建和管理 存储heap信息的文件
- WatchExecutorAndroid //[4. 可延迟执行线城池] 执行内存泄露检测的线程池executor
- HeapDumpListenerAndroid //[5. 堆信息分析器] 用于分析前面产生的dump文件,找到内存泄露的原因
- package analyzerservice //[5.1] 开启一个跨进程后台服务,并调用【leakcanary-analyzer】对堆内存信息进行分析,分析结果通过发广播方式传给另外一个后台服务
- HeapAnalyzerService 一个跨进程后台服务,并调用【leakcanary-analyzer】对堆内存信息进行分析,分析结果通过发广播方式传给另外一个后台服务
- AbstractAnalysisResultService 用于接收跨进程后台服务所传递的堆内存分析结果,触发onHeapAnalyzed(…) ps:其具体子类在[HeapDumpListenerAndroid]被实例化时构造
- ExcludedRefsAndroid //[6. 白名单map]用于排除某些系统bug导致的内存泄露
- ActivityWatcherManager 专注于监听Activity是否泄漏 ,内部使用了 application#registerActivityLifecycleCallbacks 方法来监听 onDestory 事件
我们主要看下它,改进的点:
- 泄露检测的优化,主要代码在
com.tencent.matrix.resource.watcher.ActivityRefWatcher#RetryableTask
- 增加一个一定能被回收的“哨兵”对象,用来确认系统确实进行了GC
- 直接通过WeakReference.get()来判断对象是否已被回收,避免因延迟导致误判
- 若发现某个Activity无法被回收,再重复判断3次,且要求从该Activity被记录起有2个以上的Activity被创建才认为是泄漏,以防在判断时该Activity被局部变量持有导致误判
- 对已判断为泄漏的Activity,记录其类名,避免重复提示该Activity已泄漏
- 实现了 检测和分析的分离,主要代码在
com.tencent.matrix.resource.watcher.ActivityRefWatcher#RetryableTask
- mHeapDumper存在,则进行堆内存导出,进一步上报或分析泄露位置
mHeapDumpHandler.process(heapDump)
-----CanaryWorkerService.shrinkHprofAndReport(context, result);
- mHeapDumper不存在,则不进行堆内存导出,直接上报Issue
- mHeapDumper存在,则进行堆内存导出,进一步上报或分析泄露位置
- 裁剪和上报Hprof,具体实现在:
com.tencent.matrix.resource.CanaryWorkerService
- 内存泄露分析和图片冗余分析 在
ActivityLeakAnalyzer
和DuplicatedBitmapAnalyzer
实现 - 修改了LeakCanary的引用链查找算法,使其在一次调用中能同时查找多个目标到GC Root的最短引用链
- CLIMain 是命令行分析工具的总入口
性能开销:
监测部分,在周期性轮询阶段由于是在后台线程执行的,目前设定的轮询间隔为1min。以通讯录界面和朋友圈界面的帧率作为参考,在接入ResourceCanary后2min内的平均帧率降低了10帧左右(未接入时同样时段内的平均帧率为120帧/秒),开销并不明显。 Dump Hprof阶段的开销则较大。Dump时整个App会卡死约5~15s,但考虑到ResourceCanary模块不会在线上环境启用,因此尚可接受。个人猜想Dump Hprof操作的耗时通过某些hack应该还有优化的空间;对Hprof的预处理阶段耗时约3~20s(取决于机器性能和Hprof的大小),内存开销约为1.5倍Hprof的大小,但由于是在另一个进程中完成的,而且只有在触发了Dump Hprof之后才会执行,因此对App正常运行的干扰也较小。不过改进算法使耗时和内存占用都尽可能少,还是要继续探究的。
六、IOCanaryPlugin
IO Canary: 检测文件 IO 问题,包括:
- 文件 IO 监控
a. MAIN_THREAD_IO=1, 在主线程IO超过200ms
b. BUFFER_TOO_SMALL=2, 重复读取同一个文件,同一个堆栈超过3次
c. REPEAT_IO=3, 读写文件的buffer过小,即小于4k - Closeable Leak 监控
d. CLOSE_LEAK=4, 文件泄漏
整体思路是:
-
IOCanaryPlugin 持有探针类IOCanaryCore,以执行相关启动、停止动作
-
IOCanaryCore
- 持有
Config
,内部存储相关配置信息 - 持有
CloseGuardHooker
, 用于 文件泄露检测 - 持有
IOCanaryJniBridge
, 用于IO 监控 - 持有
IOCanaryPlugin
,用于发生事件时进行通知 - 实现
OnJniIssuePublishListener
,用于在jni层发现问题时,能够触发该listener,并最终回调mIoCanaryPlugin.onDetectIssue
- 持有
-
CloseGuardHooker 文件泄露检测
- 其实现原理,主要是hook CloseGuard的相关report事件, 借助严苛模式的检测上报来实现 Matrix的监控
- 有兴趣的可以阅读 StrictMode机制以及使用场景
StrictMode,严苛模式,是Android提供的一种运行时检测机制,用于检测代码运行时的一些不规范的操作,最常见的场景是用于发现主线程的IO操作 StrictMode的实现涉及到以下源码:
- libcore/dalvik/src/main/java/dalvik/system/BlockGuard.java
- libcore/dalvik/src/main/java/dalvik/system/CloseGuard.java
- frameworks/base/core/java/android/os/StrictMode.java
Guard有“守卫”的意思,Block是阻塞的意思,在进行一些耗时操作时,譬如磁盘读写、网络操作,有一个守卫在监测着,它就是BlockGuard,如果这些耗时的操作导致主线程阻塞,BlockGuard就会发出通知; Close对应到可打开的文件,在文件被打开后,也有一个守卫在监测着,它就是CloseGuard,如果没有关闭文件,则CloseGuard就会发出通知
-
IOCanaryJniBridge
- 借助jni层的实现相关hook和unhook
- enableDetector用于控制是否开启:主线程IO耗时、重复读次数、小文件buffer读写监控
- setConfig用于控制相关配置阈值:主线程IO耗时阈值、重复读次数阈值、小文件buffer过小阈值
static void onIssuePublish
用于给jni层实现回调通知
-
cpp
- utils 获取线程id,当前堆栈等常用utils
- dector: 定义了一些探针,传入相关文件信息,以校验是否发生了目标事件
- dector 定义基础,譬如:发布事件,标记事件已发布,确认事件是否发布过等
virtual void Detect(const IOCanaryEnv& env, const IOInfo& file_io_info, std::vector<Issue>& issues) = 0;
- main_thread_detector 根据输入参数,校验主线程IO耗时, 异常事件放入
issues
中 - repeat_read_detector 根据输入参数,校验重复读次数, 异常事件放入
issues
中 - small_buffer_detector 根据输入参数,校验小文件buffer读写, 异常事件放入
issues
中
- core
- io_canary_env 主要用于读取 相关配置阈值
- io_info_collector IO事件搜集器,在发生IO事件时通过调用Collector的相关函数,构建Ioinfo进入map容器
- io_canary
- 在相应Io事件发生时,触发io_info_collector
- 在事件结束时,将收集器收集的事件s,放入queue_中
- 开启一个while(true)的循环,不断地阻塞式读queue_中的事件
- 读出来的io事件s + 构建vector,传给各个dector进行检测填充,并将其push到java层的
static void onIssuePublish
- 持有对上层的回调
OnPublishIssueCallback
,并触发
- iocanary
doHook()
在C层hook相关IO的读写操作,并触发 core#io_canary的相关IO事件监听- ProxyOpen
- ProxyOpen64
- ProxyRead
- ProxyWrite
- ProxyClose
OnIssuePublish()
回调java层触发通知doUnHook
恢复Io的相关读写操作
七、matrix-trace-canary
Trace Canary: 监控界面流畅性、启动耗时、页面切换耗时、慢函数及卡顿等问题
我们看com.tencent.matrix.trace.TracePlugin
,可以观察到四种探针:
- FPSTracer
- EvilMethodTracer
- FrameTracer
- StartUpTracer
我们还是先分析整体结构:
- config 用于获取相关配置阈值
- constants 相关配置阈值的默认值
- TracePlugin
- `init()· 根据配置,构建相关探针对象 和 ApplicationLifeObserver对象
start()
- 主线程中调用
FrameBeat.getInstance().onCreate()
- 触发相关 探针Trace的 onCreate生命周期
- 主线程中调用
stop()
- 调用
FrameBeat.getInstance().onDestroy()
- 触发相关 探针Trace的 onDestroy生命周期
- 调用
- core
- ApplicationLifeObserver
- 通过application.registerActivityLifecycleCallbacks(this)实现对相关生命周期的监听
- 通知观察者以下Activity生命周期事件:前后台切换、页面切换、create\resume\pause\start等事件
- FrameBeat 实现对FrameCallback帧频的前台监听,并通知给mFrameListeners列表
- 持有
LinkedList<IFrameBeatListener> mFrameListeners
列表 - 实现
Choreographer.FrameCallback#doFrame
(这是一个帧频通知器,往往用于界面卡顿监听),通知mFrameListeners本次帧频时间和上次帧频时间 - 实现 IFrameBeat
onCreate()
向ApplicationLifeObserver注册自身作为观察者; 前台模式时,将自身作为帧频通知器注册入android系统中onDestroy()
向ApplicationLifeObserver注销自身作为观察者;通知mFrameListeners注销事件;清除mFrameListeners;- addListener/removeListener(IFrameBeatListener listener)
- 实现
ApplicationLifeObserver.IObserver
,从而具备监听Activity生命周期的相关能力- onFront前台时,才启动监听帧频
- onBackground后台时,取消监听
- 持有
- MethodBeat
- 静态块
Hacker.hackSysHandlerCallback()
开始hook相关生命周期函数,其中HackCallback
实现了对 Activity和Application的创建感知- 主线程发送延时30s的releaseBufferMsg
- 静态对象 sReleaseBufferHandler:将buffer置空; sTimeUpdateHandler每隔5ms更新diffTime
- 持有
LinkedList<IMethodBeatListener> sListeners = new LinkedList<>()
列表 - 实现IMethodBeat
onCreate()
向ApplicationLifeObserver注册自身作为观察者; 取消主线程的releaseBufferMsg;释放一次延时循环时间线程的updateMsg(即开启周期循环任务)onDestroy()
向ApplicationLifeObserver注销自身作为观察者; 取消相关Msg;清空监听器- addListener/removeListener(IFrameBeatListener listener)
- 实现 ApplicationLifeObserver.IObserver,从而具备监听Activity生命周期的相关能力
- 前台时发送延时循环时间线程的updateMsg;后台时取消延时循环时间线程的updateMsg
- onActivityCreated 和 onActivityStarted,且处于后台状态时,发送时间线程的updateMsg
- 静态块
- ApplicationLifeObserver
- hacker
Hacker
,内部使用自定义的HackCallback,hook了"android.app.ActivityThread"
, 这个类大家应该很眼熟,Activity相关生命周期等本地进程操作都是由这个类触发HackCallback
通过动态代理handleMessage(Message msg)
方法,实现对- Activity动画进入完成时的判断,用
Hacker#isEnterAnimationComplete
静态存储 - 通过对Acitvity、CREATE_SERVICE、RECEIVER等时间监听,完成对ApplicationCreateEnd的判断,使用
HackCallback#isCreated
静态存储
- Activity动画进入完成时的判断,用
- scheduler
- LazyScheduler 一个循环周期任务,定期执行
onTimeExpire()
- LazyScheduler 一个循环周期任务,定期执行
- listeners
- IFrameBeat 帧频接收器的能力抽象
- IFrameBeatListener 给具体的探针继承实现,用于对相关信息进行分析
doFrame(long lastFrameNanos, long frameNanos)
在Choreographer.FrameCallback
中被触发,通知两次帧频的时间cancelFrame()
- IDoFrameListener 具体的掉帧监听器,在 FrameTracer中触发相关函数,异步或同步通知掉帧现象
doFrameSync
同步通知getHandler
+doFrameAsync
异步通知
- IMethodBeat 相关系统函数执行情况的通知接收器能力抽象
- IMethodBeatListener 给具体的探针继承实现,用于对相关信息进行分析
onActivityEntered
onApplicationCreated
pushFullBuffer
- Tracer
- BaseTracer
- 继承
ApplicationLifeObserver.IObserver
,从而具备感知Activity生命周期的能力 - 继承
IFrameBeatListener
,从而具备感知帧频通知的能力 - 继承
IMethodBeatListener
, 从而具备感知相关系统函数的能力 - 持有 static HashMap<Class, BaseTracer> sTracerMap,当前实例化的所有探针Tracer
- 持有 static MethodBeat sMethodBeat = new MethodBeat()
onCreate()
,将上文提及的IObserver\IFrameBeatListener\IMethodBeatListener
注册到对应的执行器中onDestroy()
,将上文提及的listeners全部注销掉sendReport
内部调用TracePlugin的onDetectIssue()
- 继承
- FrameTracer
- 持有
LinkedList<IDoFrameListener> mDoFrameListenerList = new LinkedList<>()
事件监听器 - 实现
doFrame(xxx,xxx)
, 在认为两次帧频超过阈值16.67ms时,通知mDoFrameListenerList - 实现
ViewTreeObserver.OnDrawListener
,并在Activity页面切换onChange生命周期期间注册自身,实现 isDrawing期间不检测
- 持有
- 其他的不再一一举例
- BaseTracer
由此,我们可以得出以下事件链:
TracePlugin#init
初始化时构建单例ApplicationLifeObserver监听相关Activity生命周期TracePlugin#init
初始化时构建FrameTracer、StartUpTracer、FPSTracer、EvilMethodTracer探针实例- xxTracer探针实例继承自
BaseTracer
,由此会构建其内部静态对象实例HashMap<Class<BaseTracer>, BaseTracer> sTracerMap
和MethodBeat sMethodBeat = new MethodBeat()
MethodBeat sMethodBeat = new MethodBeat()
会执行MethodBeat
其内部的静态块- 触发
Hacker.hackSysHandlerCallback()
其内部使用自定义的HackCallback,hook了"android.app.ActivityThread"
, 这个类大家应该很眼熟,Activity相关生命周期等本地进程操作都是由这个类触发 HackCallback
实现了对 Activity和Application的创建感知
- 触发
- xxTracer探针实例继承自
TracePlugin#start()
在主进程中调用FrameBeat.getInstance().onCreate()
onCreate
向ApplicationLifeObserver注册自身FrameBeat作为观察者; 前台模式时,将自身作为帧频通知器注册入android系统中- 同时,FrameBeat会在帧频通知时,触发
LinkedList<IFrameBeatListener> mFrameListeners
监听列表
TracePlugin#start()
中调用mFrameTracer.onCreate();
onCreate
其实现在BaseTracer中,将自身作为IObserver\IFrameBeatListener\IMethodBeatListener
注册到对应的执行器中,从而具备感知Activity生命周期的能力、感知帧频通知的能力、感知相关系统函数的能力- 同时,实现
doFrame(xxx,xxx)
, 在认为两次帧频超过阈值16.67ms时,通知mDoFrameListenerList - 同时,实现
ViewTreeObserver.OnDrawListener
,并在Activity页面切换onChange生命周期期间注册自身,实现 isDrawing期间不检测
TracePlugin#start()
中调用mEvilMethodTracer.onCreate();
onCreate
其实现在BaseTracer中,将自身作为IObserver\IFrameBeatListener\IMethodBeatListener
注册到对应的执行器中,从而具备感知Activity生命周期的能力、感知帧频通知的能力、感知相关系统函数的能力onCreate
构建相关线程对象、周期循环任务对象等- ANR检测 与 Normal检测
- 同时,实现
doFrame(xxx,xxx)
, 取消上次延时任务,并开启一次延时任务,这也就是说 如果一个函数在一次帧频间隔期间完成,那么就不会触发延时任务;当触发延时任务时,其实也意味着主线程出现了耗时操作,并注明Type.ANR, anr超时场景 - 同时,实现
LazyScheduler.ILazyTask
实现延时任务,即打印当前堆栈信息或其他分析(调用evilMethodTracer.handleBuffer
并注明Type.ANR) - 同时,实现
doFrame(xxx,xxx)
中会比对两次doFrame的时间,如果超过阈值则认为出现了NORMAL耗时
- 同时,实现
- STARTUP耗时检测
- 同时,StartUpTracer会在应用启动时校验启动耗时,并调用
evilMethodTracer.handleBuffer
并注明Type.STARTUP
- 同时,StartUpTracer会在应用启动时校验启动耗时,并调用
- Enter检测
- 同时,实现
onActivityCreated
,记录界面进入时间 - 同时,实现
onActivityEntered
,比对进入时间,并用evilMethodTracer.handleBuffer
并注明Type.Enter
- 同时,实现
TracePlugin#start()
中调用StartUpTracer.onCreate();
onCreate
其实现在BaseTracer中,将自身作为IObserver\IFrameBeatListener\IMethodBeatListener
注册到对应的执行器中,从而具备感知Activity生命周期的能力、感知帧频通知的能力、感知相关系统函数的能力- 同时,实现
onActivityCreated
,监听第一个FirstActivityName - 同时,实现
onActivityEntered
,进行启动慢函数检测和启动监控上报
TracePlugin#start()
中调用FPSTracer.onCreate();
统计Acitivty的帧频情况
八、matrix-sqlite-lint
SQLiteLint在APP运行时进行检测,而且大部分检测算法与数据量无关即不依赖线上的数据状态。只要你触发了某条sql语句的执行,SQLiteLint就会帮助你review这条语句是否写得有问题。而这在开发、测试或者灰度阶段就可以进行。
简单而言,sqliteLint主要是在运行时通过切入数据库框架的sql语句解析层,通过织入相关的Checker,根据sql语句结构和数据库结构来分析sql语句的性能,给出不同等级的建议
源码涉及的较多,本文暂不分析,留待下一章节
九、APK Checker
Matrix-ApkChecker以一个jar包的形式提供使用,通过命令行执行 java -jar ApkChecker.jar 即可运行。
- 具有更好的可用性:JAR 包方式提供,更方便应用到持续集成系统中,从而追踪和对比每个 APK 版本之间的变化
- 更多的检查分析功能:除具备 APKAnalyzer 的功能外,还支持统计 APK 中包含的 R 类、检查是否有多个动态库静态链接了 STL 、搜索 APK 中包含的无用资源,以及支持自定义检查规则等
- 输出的检查结果更加详实:支持可视化的 HTML 格式,便于分析处理的 JSON ,自定义输出等等
可以参看官方的文档:Matrix ApkChecker