(4.2.49)微信APM:Matrix源码浅析

Matrix 是一款微信研发并日常使用的 APM(Application Performance Manage),当前主要运行在 Android 平台上

一、官方地址

二、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 现在的描述,它提供的运行时监控能力:

  1. Resource Canary:
    1. Activity 泄漏
    2. Bitmap 冗余
  2. Trace Canary
    1. 界面流畅性
    2. 启动耗时
    3. 页面切换耗时
    4. 慢函数
    5. 卡顿
  3. SQLite Lint: 按官方最佳实践自动化检测 SQLite 语句的使用质量
  4. IO Canary: 检测文件 IO 问题
    1. 文件 IO 监控
    2. Closeable Leak 监控

四、matrix-android-lib

  • Plugin 被定义为某种监控能力的抽象
  • Issue 发生的某种被监控事件
  • Report 一种观察者模式的实现,用于发现Issue时,通知观察者
  • Matrix 单例模式的实现,暴漏给外部的接口
  • matrix-config.xml 相关监控的配置项
  • IDynamicConfig 开放给用户自定义的相关监控的配置项,其实例会被各个Plugin持有

4.1 Plugin监控能力

  • IPlugin 一种监控能力的抽象
  • PluginListener 开放给用户的,感知监控模块初始化、启动、停止、发现问题等生命周期的能力。 用户可自行实现,并注入到Matrix中
  • Plugin
    1. 对IPlugin的默认实现,以触发相关PluginListener生命周期
      • 这也就意味著:监控能力生命周期的触发,是在监控能力具体执行动作之前出发的
    2. 实现 IssuePublisher.OnIssueDetectListener接口的onDetectIssue方法
      • 该方法会在 具体监控事件发生时,被 Matrix.with().getPluginByClass(xxx).onDetectIssue(Issue)这样调用。注意这里是第一种通知方式
      • 该方法内部对Issue相关环境变量进行赋值,譬如引发该Issue的Plugin信息
      • 该方法最终触发PluginListener#onReportIssue
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 被监控事件
    1. type:类型,用于区分同一个tag不同类型的上报
    2. tag: 该上报对应的tag
    3. stack:该上报对应的堆栈
    4. process:该上报对应的进程名
    5. 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
    1. 先使用HeapDumper进行 堆内存导出
      2.开启一个跨进程后台服务HeapAnalyzerService,该后台服务启动后,调用【leakcanary-analyzer】对堆内存信息进行分析
    2. 分析结果通过发广播方式传给另外一个后台服务AbstractAnalysisResultService

涉及到的对象如下:

  • 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
  • 裁剪和上报Hprof,具体实现在:com.tencent.matrix.resource.CanaryWorkerService
  • 内存泄露分析和图片冗余分析 在ActivityLeakAnalyzerDuplicatedBitmapAnalyzer 实现
    • 从Hprof文件中获取所有冗余的Bitmap对象
    • 这个功能Android Monitor已经有完整实现了,原理简单粗暴——把所有未被回收的Bitmap的数据buffer取出来,然后先对比所有长度为1的buffer,找出相同的,记录所属的Bitmap对象;再对比所有长度为2的、长度为3的buffer……直到把所有buffer都比对完,这样就记录下了所有冗余的Bitmap对象了,接着再套用LeakCanary获取引用链的逻辑把这些Bitmap对象到GC Root的最短强引用链找出来即可。
  • 修改了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 问题,包括:

  1. 文件 IO 监控
    a. MAIN_THREAD_IO=1, 在主线程IO超过200ms
    b. BUFFER_TOO_SMALL=2, 重复读取同一个文件,同一个堆栈超过3次
    c. REPEAT_IO=3, 读写文件的buffer过小,即小于4k
  2. Closeable Leak 监控
    d. CLOSE_LEAK=4, 文件泄漏

整体思路是:

  1. IOCanaryPlugin 持有探针类IOCanaryCore,以执行相关启动、停止动作

  2. IOCanaryCore

    1. 持有 Config,内部存储相关配置信息
    2. 持有 CloseGuardHooker, 用于 文件泄露检测
    3. 持有 IOCanaryJniBridge, 用于IO 监控
    4. 持有 IOCanaryPlugin,用于发生事件时进行通知
    5. 实现 OnJniIssuePublishListener,用于在jni层发现问题时,能够触发该listener,并最终回调mIoCanaryPlugin.onDetectIssue
  3. 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就会发出通知
  1. IOCanaryJniBridge

    • 借助jni层的实现相关hook和unhook
    • enableDetector用于控制是否开启:主线程IO耗时、重复读次数、小文件buffer读写监控
    • setConfig用于控制相关配置阈值:主线程IO耗时阈值、重复读次数阈值、小文件buffer过小阈值
    • static void onIssuePublish 用于给jni层实现回调通知
  2. 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
        1. 在相应Io事件发生时,触发io_info_collector
        2. 在事件结束时,将收集器收集的事件s,放入queue_中
        3. 开启一个while(true)的循环,不断地阻塞式读queue_中的事件
        4. 读出来的io事件s + 构建vector,传给各个dector进行检测填充,并将其push到java层的static void onIssuePublish
        5. 持有对上层的回调 OnPublishIssueCallback,并触发
    • iocanary
      1. doHook()在C层hook相关IO的读写操作,并触发 core#io_canary的相关IO事件监听
        • ProxyOpen
        • ProxyOpen64
        • ProxyRead
        • ProxyWrite
        • ProxyClose
      2. OnIssuePublish() 回调java层触发通知
      3. doUnHook恢复Io的相关读写操作

七、matrix-trace-canary

Trace Canary: 监控界面流畅性、启动耗时、页面切换耗时、慢函数及卡顿等问题

我们看com.tencent.matrix.trace.TracePlugin,可以观察到四种探针:

  1. FPSTracer
  2. EvilMethodTracer
  3. FrameTracer
  4. StartUpTracer

我们还是先分析整体结构:

  • config 用于获取相关配置阈值
  • constants 相关配置阈值的默认值
  • TracePlugin
    • `init()· 根据配置,构建相关探针对象 和 ApplicationLifeObserver对象
    • start()
      1. 主线程中调用 FrameBeat.getInstance().onCreate()
      2. 触发相关 探针Trace的 onCreate生命周期
    • stop()
      1. 调用 FrameBeat.getInstance().onDestroy()
      2. 触发相关 探针Trace的 onDestroy生命周期
  • core
    • ApplicationLifeObserver
      1. 通过application.registerActivityLifecycleCallbacks(this)实现对相关生命周期的监听
      2. 通知观察者以下Activity生命周期事件:前后台切换、页面切换、create\resume\pause\start等事件
    • FrameBeat 实现对FrameCallback帧频的前台监听,并通知给mFrameListeners列表
      1. 持有LinkedList<IFrameBeatListener> mFrameListeners列表
      2. 实现Choreographer.FrameCallback#doFrame(这是一个帧频通知器,往往用于界面卡顿监听),通知mFrameListeners本次帧频时间和上次帧频时间
      3. 实现 IFrameBeat
        1. onCreate() 向ApplicationLifeObserver注册自身作为观察者; 前台模式时,将自身作为帧频通知器注册入android系统中
        2. onDestroy()向ApplicationLifeObserver注销自身作为观察者;通知mFrameListeners注销事件;清除mFrameListeners;
        3. addListener/removeListener(IFrameBeatListener listener)
      4. 实现ApplicationLifeObserver.IObserver,从而具备监听Activity生命周期的相关能力
        1. onFront前台时,才启动监听帧频
        2. onBackground后台时,取消监听
    • MethodBeat主要做的事是用一个数组记录应用执行的每个方法和方法耗时,用的是long[],使用方法id和耗时进行按位换算得要一个long值作为元素,缓存默认长度是一百万
      1. 静态块
        1. Hacker.hackSysHandlerCallback()开始hook相关生命周期函数,其中HackCallback 实现了对 Activity和Application的创建感知
        2. 主线程发送延时30s的releaseBufferMsg
      2. 静态对象 sReleaseBufferHandler:将buffer置空; sTimeUpdateHandler每隔5ms更新diffTime
      3. 持有LinkedList<IMethodBeatListener> sListeners = new LinkedList<>()列表
      4. 实现IMethodBeat
        1. onCreate() 向ApplicationLifeObserver注册自身作为观察者; 取消主线程的releaseBufferMsg;释放一次延时循环时间线程的updateMsg(即开启周期循环任务)
        2. onDestroy()向ApplicationLifeObserver注销自身作为观察者; 取消相关Msg;清空监听器
        3. addListener/removeListener(IFrameBeatListener listener)
      5. 实现 ApplicationLifeObserver.IObserver,从而具备监听Activity生命周期的相关能力
        1. 前台时发送延时循环时间线程的updateMsg;后台时取消延时循环时间线程的updateMsg
        2. onActivityCreated 和 onActivityStarted,且处于后台状态时,发送时间线程的updateMsg
      6. i()o() 产生数据
        1. 在应用运行的每个方法之前调用i(),在每个方法结尾调用方法o(),记录方法名称和o-i的时间差。这两个方法的最后会执行mergeData方法
        2. 具体调用在matrix-gradle-plugin库里MethodTracer这个类,在于使用ASM进行字节码操作,常用于切面编程
  • hacker
    • Hacker,内部使用自定义的HackCallback,hook了"android.app.ActivityThread", 这个类大家应该很眼熟,Activity相关生命周期等本地进程操作都是由这个类触发
    • HackCallback通过动态代理handleMessage(Message msg)方法,实现对
      1. Activity动画进入完成时的判断,用Hacker#isEnterAnimationComplete静态存储
      2. 通过对Acitvity、CREATE_SERVICE、RECEIVER等时间监听,完成对ApplicationCreateEnd的判断,使用HackCallback#isCreated静态存储
  • scheduler
    • LazyScheduler 一个循环周期任务,定期执行onTimeExpire()
  • listeners
    • IFrameBeat 帧频接收器的能力抽象
    • IFrameBeatListener 给具体的探针继承实现,用于对相关信息进行分析
      1. doFrame(long lastFrameNanos, long frameNanos)Choreographer.FrameCallback中被触发,通知两次帧频的时间
      2. cancelFrame()
    • IDoFrameListener 具体的掉帧监听器,在 FrameTracer中触发相关函数,异步或同步通知掉帧现象
      1. doFrameSync 同步通知
      2. getHandler + doFrameAsync 异步通知
    • IMethodBeat 相关系统函数执行情况的通知接收器能力抽象
    • IMethodBeatListener 给具体的探针继承实现,用于对相关信息进行分析
      1. onActivityEntered
      2. onApplicationCreated
      3. pushFullBuffer
  • Tracer
    • BaseTracer
      1. 继承 ApplicationLifeObserver.IObserver,从而具备感知Activity生命周期的能力
      2. 继承 IFrameBeatListener,从而具备感知帧频通知的能力
      3. 继承 IMethodBeatListener, 从而具备感知相关系统函数的能力
      4. 持有 static HashMap<Class, BaseTracer> sTracerMap,当前实例化的所有探针Tracer
      5. 持有 static MethodBeat sMethodBeat = new MethodBeat()
      6. onCreate(),将上文提及的 IObserver\IFrameBeatListener\IMethodBeatListener注册到对应的执行器中
      7. onDestroy(),将上文提及的listeners全部注销掉
      8. sendReport内部调用TracePlugin的onDetectIssue()
    • FrameTracer
      1. 持有LinkedList<IDoFrameListener> mDoFrameListenerList = new LinkedList<>() 事件监听器
      2. 实现 doFrame(xxx,xxx), 在认为两次帧频超过阈值16.67ms时,通知mDoFrameListenerList
      3. 实现ViewTreeObserver.OnDrawListener,并在Activity页面切换onChange生命周期期间注册自身,实现 isDrawing期间不检测
    • 其他的不再一一举例

由此,我们可以得出以下事件链:

  1. TracePlugin#init初始化时构建单例ApplicationLifeObserver监听相关Activity生命周期
  2. TracePlugin#init初始化时构建FrameTracer、StartUpTracer、FPSTracer、EvilMethodTracer探针实例
    1. xxTracer探针实例继承自BaseTracer,由此会构建其内部静态对象实例HashMap<Class<BaseTracer>, BaseTracer> sTracerMapMethodBeat sMethodBeat = new MethodBeat()
      1. MethodBeat sMethodBeat = new MethodBeat() 会执行MethodBeat其内部的静态块
        1. 触发Hacker.hackSysHandlerCallback() 其内部使用自定义的HackCallback,hook了"android.app.ActivityThread", 这个类大家应该很眼熟,Activity相关生命周期等本地进程操作都是由这个类触发
        2. HackCallback 实现了对 Activity和Application的创建感知
  3. TracePlugin#start()在主进程中调用 FrameBeat.getInstance().onCreate()
    1. onCreate向ApplicationLifeObserver注册自身FrameBeat作为观察者; 前台模式时,将自身作为帧频通知器注册入android系统中
    2. 同时,FrameBeat会在帧频通知时,触发LinkedList<IFrameBeatListener> mFrameListeners监听列表
  4. TracePlugin#start()中调用mFrameTracer.onCreate();
    1. onCreate其实现在BaseTracer中,将自身作为 IObserver\IFrameBeatListener\IMethodBeatListener注册到对应的执行器中,从而具备感知Activity生命周期的能力、感知帧频通知的能力、感知相关系统函数的能力
    2. 同时,实现 doFrame(xxx,xxx), 在认为两次帧频超过阈值16.67ms时,通知mDoFrameListenerList
    3. 同时,实现ViewTreeObserver.OnDrawListener,并在Activity页面切换onChange生命周期期间注册自身,实现 isDrawing期间不检测
  5. TracePlugin#start()中调用mEvilMethodTracer.onCreate();
    1. onCreate其实现在BaseTracer中,将自身作为 IObserver\IFrameBeatListener\IMethodBeatListener注册到对应的执行器中,从而具备感知Activity生命周期的能力、感知帧频通知的能力、感知相关系统函数的能力
    2. onCreate 构建相关线程对象、周期循环任务对象等
    3. ANR检测 与 Normal检测
      1. 同时,实现 doFrame(xxx,xxx), 取消上次延时任务,并开启一次延时任务,这也就是说 如果一个函数在一次帧频间隔16.67ms期间完成,那么就不会触发延时任务;当触发延时任务时,其实也意味着主线程出现了耗时操作,并注明Type.ANR, anr超时场景
      2. 同时,实现LazyScheduler.ILazyTask实现延时任务,即打印当前堆栈信息或其他分析(调用evilMethodTracer.handleBuffer并注明Type.ANR)
      3. 同时,实现 doFrame(xxx,xxx)中会比对两次doFrame的时间,如果超过阈值则认为出现了NORMAL耗时
    4. STARTUP耗时检测
      1. 同时,StartUpTracer会在应用启动时校验启动耗时,并调用evilMethodTracer.handleBuffer并注明Type.STARTUP
    5. Enter检测
      1. 同时,实现onActivityCreated,记录界面进入时间
      2. 同时,实现onActivityEntered,比对进入时间,并用evilMethodTracer.handleBuffer并注明Type.Enter
  6. TracePlugin#start()中调用StartUpTracer.onCreate();
    1. onCreate其实现在BaseTracer中,将自身作为 IObserver\IFrameBeatListener\IMethodBeatListener注册到对应的执行器中,从而具备感知Activity生命周期的能力、感知帧频通知的能力、感知相关系统函数的能力
    2. 同时,实现 onActivityCreated,监听第一个FirstActivityName
    3. 同时,实现 onActivityEntered,进行启动慢函数检测和启动监控上报
  7. 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

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值