写在前面

       近期开始 Android Framework 层的学习,然而较为庞大的 Framework 让人感觉无从下手。碰巧看到一篇文章说到腾讯的 性能监控框架 Matrix 用到了大量 Framework 相关的知识,所以试着分析下该框架的源码实现。

在学习大佬们代码的同时主要关注该框架用到了哪些、是怎么使用的 Framework 的内容。

一、Matrix 简介

官方说明

Matrix 是一款微信研发并日常使用的应用性能接入框架,支持iOS, macOS和Android。Matrix 通过接入各种性能监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。

大公司就是大气,直接双端都给你整一套。

Matrix 地址

http://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2FTencent%2Fmatrix

Matrix for Android

Matrix-android 当前监控范围包括:应用安装包大小,帧率变化,启动耗时,卡顿,慢方法,SQLite 操作优化,文件读写,内存泄漏等等。

  • APK Checker: 针对 APK 安装包的分析检测工具,根据一系列设定好的规则,检测 APK 是否存在特定的问题,并输出较为详细的检测结果报告,用于分析排查问题以及版本追踪
  • Resource Canary: 基于 WeakReference 的特性和 Square Haha 库开发的 Activity 泄漏和 Bitmap 重复创建检测工具
  • Trace Canary: 监控界面流畅性、启动耗时、页面切换耗时、慢函数及卡顿等问题
  • SQLite Lint: 按官方最佳实践自动化检测 SQLite 语句的使用质量
  • IO Canary: 检测文件 IO 问题,包括:文件 IO 监控和 Closeable Leak 监控

好家伙,功能还真不少。看样子是个大工程,排个计划吧:

  1. 首先是对框架的大致了解,对框架中用到的需要用到的类、函数进行预习;
  2. 从某一模块入手,分析功能实现的同时注重 Framework 的内容;
  3. 最后进行总结,思考为什么这样做,有没有更好的做法。

那么本文先大概了解一下框架,遇到 Framework 中的知识进行简单的了解和预习。

二、使用 Matrix

有关 Matrix 的接入和使用官方文档已经写得很清楚了,本文简单总结下:

  1. 引入 Matrix 库,添加相关依赖;
  2. 创建插件监听,可以接收到插件的启动和工作通知。
    Matrix 的功能基本都是由这些 插件 Plugin 实现的,这样做的好处一方面是解耦,另一方面是用户可以根据需要选择使用的功能。
  3. 在 Application 中初始化 Matrix,添加插件并开启插件功能。

三、Matrix 结构

接下来根据 Matrix 的创建和使用来确定它的结构。

初始化

Matrix 需要在 Applicaton 中初始化,对象的构建方式是熟悉的建造者模式:

public class MatrixApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        // 创建 Matrix,传入 application        Matrix.Builder builder = new Matrix.Builder(this);        // 设置插件监听        builder.patchListener(new TestPluginListener(this));        // 创建插件        TracePlugin tracePlugin = new TracePlugin(new TraceConfig.Builder()                .build());        // 添加插件        builder.plugin(tracePlugin);        // 初始化        Matrix.init(builder.build());        // 插件开始工作        tracePlugin.start();    }}

维护的变量也比较简单:

public static class Builder {    // 持有 Application     private final Application application;    // 插件工作回调    private PluginListener pluginListener;    // 维护插件列表    private HashSet plugins = new HashSet<>();    public Builder(Application app) {        if (app == null) {            throw new RuntimeException("matrix init, application is null");        }        this.application = app;    }}
  • PluginListener:是一个接口,定义了插件的生命周期。官方提供了默认实现 DefaultPluginListener,我们只需要继承该类并设置给 Matrix 就可以接收到插件的生命周期。
public interface PluginListener {    void onInit(Plugin plugin);// 初始化    void onStart(Plugin plugin);// 开始    void onStop(Plugin plugin);// 结束    void onDestroy(Plugin plugin);// 销毁    void onReportIssue(Issue issue);// 提交报告}
public class TestPluginListener extends DefaultPluginListener {    public static final String TAG = "Matrix.TestPluginListener";    public TestPluginListener(Context context) {        super(context);    }    @Override    public void onReportIssue(Issue issue) {        super.onReportIssue(issue);        MatrixLog.e(TAG, issue.toString());        //add your code to process data    }}
  • plugins:插件列表,使用 HashSet 维护,保证插件不会重复添加。

插件 Plugin

插件是 Matrix 的重要组成结构,通过继承抽象类 Plugin 来创建一个插件,Plugin 是接口 IPlugin 的实现。IPlugin 接口定义了插件所实现的主要功能:

public interface IPlugin {    Application getApplication();    void init(Application application, PluginListener pluginListener);    void start();    void stop();    void destroy();    String getTag();    void onForeground(boolean isForeground);}

比如分析卡顿的 TracePlugin,它就是一个继承了 Plugin 的插件实现,在工作的过程中也会调用这些方法。

Matrix 构造器

Matrix.Builder builder = new Matrix.Builder(this);Matrix.init(builder.build());

调用 builder.build() 之后会创建一个 Matrix 对象,然后创建一个用于监听 App 生命周期的 AppActiveMatrixDelegate。之后遍历所有的插件列表,并调用它们的 init() 方法初始化插件。

private Matrix(Application app, PluginListener listener, HashSet plugins) {    this.application = app;    this.pluginListener = listener;    this.plugins = plugins;    // 初始化    AppActiveMatrixDelegate.INSTANCE.init(application);    for (Plugin plugin : plugins) {        plugin.init(application, pluginListener);        pluginListener.onInit(plugin);    }}

AppActiveMatrixDelegate

这个类是个枚举单例,并且监听应用 Activity 生命周期以及内存状态。

public enum AppActiveMatrixDelegate {    // 1. 利用枚举创建单例    INSTANCE;    private static final String TAG = "Matrix.AppActiveDelegate";    private final Set listeners = new HashSet();    private boolean isAppForeground = false;    private String visibleScene = "default";    private Controller controller = new Controller();    private boolean isInit = false;    private String currentFragmentName;    private Handler handler;    public void init(Application application) {        if (isInit) {            MatrixLog.e(TAG, "has inited!");            return;        }        this.isInit = true;        // 2. HandlerTherad:一个封装了 Handler 的线程        if (null != MatrixHandlerThread.getDefaultHandlerThread()) {            this.handler = new Handler(MatrixHandlerThread.getDefaultHandlerThread().getLooper());        }        // 3. 注册应用内存状态回调        application.registerComponentCallbacks(controller);        // 4. 注册监听 Activity 生命周期        application.registerActivityLifecycleCallbacks(controller);    }}
  1. 枚举实现单例的原理是利用枚举的特点来实现的:枚举类型是线程安全的,并且只会装载一次。
  2. HandlerTherad 继承了 Thread,可以看作是一个线程。而其内部维护了一个 Handler,在调用 start() 方法后初始化 Looper,可以很方便地执行异步任务。其它类可以通过 getThreadHandler 方法获取 HandlerTherad 的 Handler,然后 post 任务由 HandlerTherad 内部的 Looper 取出并执行。
  3. registerComponentCallbacks:Application 的方法,作用是监听应用的内存状态。
    在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用 onLowMemory。
    OnTrimMemory 是 Android 4.0 之后提供的 API。比起 onLowMemory,这个回调新增返回了一个 int 值表示当前内存状态,开发者可以根据返回的状态来适当回收资源避免 app 被杀死的风险。想要监听内存状态回调需要实现 ComponentCallbacks2 接口,该接口是 ComponentCallbacks 的升级版。

应用内存优化之OnLowMemory&OnTrimMemory             

  1. registerActivityLifecycleCallbacks:注册监听 Activity 状态回调,实现 Application.ActivityLifecycleCallbacks 接口以监听 Activity 生命周期回调。
Controller

Controller 实现 ComponentCallbacks2 监听内存状态、ActivityLifecycleCallbacks 监听 Activity 生命周期。

private final class Controller implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {    @Override    public void onActivityStarted(Activity activity) {        // 1. 记录启动的 Activity        updateScene(activity);        // 1.1 告知 listeners Activity 在前台了        onDispatchForeground(getVisibleScene());    }    @Override    public void onActivityStopped(Activity activity) {        // 1.2 获取栈顶活动的 Activity        if (getTopActivityName() == null) {            // 1.3 告知 listeners Activity 在后台了            onDispatchBackground(getVisibleScene());        }    }    ...    @Override    public void onTrimMemory(int level) {        MatrixLog.i(TAG, "[onTrimMemory] level:%s", level);        // 2. TRIM_MEMORY_UI_HIDDEN 表示当前 app UI 不再可见        if (level == TRIM_MEMORY_UI_HIDDEN && isAppForeground) { // fallback            onDispatchBackground(visibleScene);        }    }}

Controller 的逻辑主要为了区分 App 进入前台或后台。怎么区分呢?

  • 有 Activity 回调了 onStart,说明 App 进入了前台,记录并返回给监听就行;
  • 有 Activity 回调了 onStop,且栈顶没有 Resume 状态的 Activity,说明 App 进入了后台;
    用户点击了 Home 或者 Back 键,系统会通过 onTrimMemory 回调一个 TRIM_MEMORY_UI_HIDDEN 状态,告知这是 App 进入后台,是回收资源的大好时机。

回调 onStart 之后用一个字符串记录当前 Activity

private void updateScene(Activity activity) {    visibleScene = activity.getClass().getName();}public String getVisibleScene() {    return visibleScene;}

我们主要关注 onActivityStopped() 回调中的 getTopActivityName() 方法,该方法用于获取栈顶活动状态的 Activity。

getTopActivityName()

public static String getTopActivityName() {    long start = System.currentTimeMillis();    try {        // 获取 ActivityThread Class 对象        Class activityThreadClass = Class.forName("android.app.ActivityThread");        // 调用这个类的 currentActivityThread 方法,返回一个静态的 ActivityThread 实例 sCurrentActivityThread        // 这个静态实例是在 main 函数中赋值的        Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);        // 获取 ActivityThread 的 mActivities 列表        // 在 Activity onCreate 之后,往列表添加 Activity 记录        Field activitiesField = activityThreadClass.getDeclaredField("mActivities");        activitiesField.setAccessible(true);        Map<Object, Object> activities; // 获取 activityThread 类的 mActivities 对象        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {            activities = (HashMap<Object, Object>) activitiesField.get(activityThread);        } else {            activities = (ArrayMap<Object, Object>) activitiesField.get(activityThread);        }        if (activities.size() < 1) {            return null;        }        for (Object activityRecord : activities.values()) {            Class activityRecordClass = activityRecord.getClass();            Field pausedField = activityRecordClass.getDeclaredField("paused");            pausedField.setAccessible(true);            if (!pausedField.getBoolean(activityRecord)) {// onResume 的 Activity paused 为 false                Field activityField = activityRecordClass.getDeclaredField("activity");                activityField.setAccessible(true);                Activity activity = (Activity) activityField.get(activityRecord);                return activity.getClass().getName();            }        }    } catch (Exception e) {        e.printStackTrace();    } finally {        long cost = System.currentTimeMillis() - start;        MatrixLog.d(TAG, "[getTopActivityName] Cost:%s", cost);    }    return null;}

整个过程就是利用反射操作 Framework ActivityThread 的参数和函数,获取栈顶的非 paused 状态的 Activity。这段代码需要看着 ActivityThread 类慢慢消化,在你的 IDE 查看或者在线查看。

综上,AppActiveMatrixDelegate 是利用内部的 Controller 监听 App 发来的信号,用来确定应用程序的前后台状态。

外部可以设置监听,等应用程序前后台转换的时候再遍历监听者回调告知。

Issue

当插件监控到 App 运行出现问题时,会把问题信息封装为一个 Issue 类进行报告。

public class Issue {    private int        type;    private String     tag;    private String     key;    private JSONObject content;    private Plugin     plugin;    public static final String ISSUE_REPORT_TYPE    = "type";    public static final String ISSUE_REPORT_TAG     = "tag";    public static final String ISSUE_REPORT_PROCESS = "process";    public static final String ISSUE_REPORT_TIME = "time";}

可以看到该类详细记录了问题的类型、信息、插件信息等,发现问题是怎么报告呢?我们拿性能监控插件 TracePlugin 中的 FrameTracer 举例:

FrameTracer

void report() {    float fps = Math.min(60.f, 1000.f * sumFrame / sumFrameCost);    MatrixLog.i(TAG, "[report] FPS:%s %s", fps, toString());    try {        // 根据插件名称遍历查找        TracePlugin plugin = Matrix.with().getPluginByClass(TracePlugin.class);        if (null == plugin) {            return;        }        // ... 省略部分代码                JSONObject resultObject = new JSONObject();        resultObject = DeviceUtil.getDeviceInfo(resultObject, plugin.getApplication());        // 组装内容        resultObject.put(SharePluginInfo.ISSUE_SCENE, visibleScene);        resultObject.put(SharePluginInfo.ISSUE_DROP_LEVEL, dropLevelObject);        resultObject.put(SharePluginInfo.ISSUE_DROP_SUM, dropSumObject);        resultObject.put(SharePluginInfo.ISSUE_FPS, fps);        Issue issue = new Issue();        issue.setTag(SharePluginInfo.TAG_PLUGIN_FPS);        issue.setContent(resultObject);        // 调用插件方法        plugin.onDetectIssue(issue);    } catch (JSONException e) {        MatrixLog.e(TAG, "json error", e);    } finally {        sumFrame = 0;        sumDroppedFrames = 0;        sumFrameCost = 0;    }}

最后会调用 Plugin 的 onDetectIssue (Detect:发现、侦查出)方法传递 Issue 信息。

Plugin # onDetectIssue

@Overridepublic void onDetectIssue(Issue issue) {    if (issue.getTag() == null) {        // 设置默认 tag        issue.setTag(getTag());    }    issue.setPlugin(this);    JSONObject content = issue.getContent();    // add tag and type for default    try {        if (issue.getTag() != null) {            content.put(Issue.ISSUE_REPORT_TAG, issue.getTag());        }        if (issue.getType() != 0) {            content.put(Issue.ISSUE_REPORT_TYPE, issue.getType());        }        content.put(Issue.ISSUE_REPORT_PROCESS, MatrixUtil.getProcessName(application));        content.put(Issue.ISSUE_REPORT_TIME, System.currentTimeMillis());    } catch (JSONException e) {        MatrixLog.e(TAG, "json error", e);    }    // 报告 Issue    pluginListener.onReportIssue(issue);}

这个 pluginListener 对象其实就是在 初始化 的时候创建并设置的 TestPluginListener,现在发现问题了就通过这个 Listener 报告问题。

public class TestPluginListener extends DefaultPluginListener {    public static final String TAG = "Matrix.TestPluginListener";    public TestPluginListener(Context context) {        super(context);    }    @Override    public void onReportIssue(Issue issue) {        super.onReportIssue(issue);        MatrixLog.e(TAG, issue.toString());        // 收到 Issue,做后续工作    }}

总结

画个简单的流程图:

android studio 发布的maven在哪里 android studio my application_初始化

流程

到这里,Matrix 大致的工作流程已经搞清楚了。但是到现在基本没有接触核心功能,Matrix 是怎么分析卡顿的?怎么分析 ANR 的?... 后面会发文继续分析,敬请期待。

作者:Marker_Sky