Android端腾讯性能监控框架Matrix源码分析之第一篇

腾讯性能监控框架Matrix源码分析之第一篇

概述

前几天腾讯将一款Android应用性能监控的框架matrix开源了,源码地址在https://github.com/Tencent/matrix,作者是微信终端团队。matrix到底是什么?据官方说法如下:
Matrix 是一款微信研发并日常使用的 APM(Application Performance Manage),当前主要运行在 Android 平台上。 Matrix 的目标是建立统一的应用性能接入框架,通过各种性能监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。
Matrix 当前监控范围包括:应用安装包大小,帧率变化,启动耗时,卡顿,慢方法,SQLite 操作优化,文件读写,内存泄漏等等(此段截取自matrix的GitHub介绍)
下面直接看源码:

代码的入口在application的onCreate()里进行初始化的,

Matrix.Builder builder = new Matrix.Builder(this);
```
//省略了一部分构造器创建对象的一段代码,这里仅说明是入口
```
Matrix.init(builder.build());

和leakcanary等库一样在application初始化,Matrix的创建采用了常用的构造器模式,现在进入Matrix内部看看

private static volatile Matrix sInstance;

private final HashSet<Plugin> plugins;//插件集合
private final Application     application;
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 static void setLogIml(MatrixLog.MatrixLogImp imp) {
    MatrixLog.setMatrixLogImp(imp);
}

public static boolean isInstalled() {
    return sInstance != null;
}

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;
}

matrix里持有一个插件的集合plugins,使用的是hashSet来保证不出现重复,还有一个plugin状态的监听pluginListener。Matrix采用了单例模式,volatile sInstance保证线程可见行,初始化的时候采用了双重检查,在构造函数里给变量赋值并遍历plugins集合,并逐个调用插件的初始化方法plugin.init()
那插件plugin是什么呢?下面是plugin的代码:

public abstract class Plugin implements IPlugin, IssuePublisher.OnIssueDetectListener {
private static final String TAG = "Matrix.Plugin";

public static final int PLUGIN_CREATE    = 0x00;
public static final int PLUGIN_INITED    = 0x01;
public static final int PLUGIN_STARTED   = 0x02;
public static final int PLUGIN_STOPPED   = 0x04;
public static final int PLUGIN_DESTROYED = 0x08;
private PluginListener pluginListener;
private Application    application;
private boolean isSupported = true;
private int status = PLUGIN_CREATE;
@Override
public void init(Application app, PluginListener listener) {
    if (application != null || pluginListener != null) {
        throw new RuntimeException("plugin duplicate init, application or plugin listener is not null");
    }
    status = PLUGIN_INITED;
    this.application = app;
    this.pluginListener = listener;
}
@Override
public void onDetectIssue(Issue issue) {
    pluginListener.onReportIssue(issue);
}
public Application getApplication() {
    return application;
}
@Override
public void start() {
    //省略部分代码
    pluginListener.onStart(this);
}
@Override
public void stop() {
    //省略部分代码
    pluginListener.onStop(this);
}

@Override
public void destroy() {
    //省略部分代码
    pluginListener.onDestroy(this);
}
  }

plugin它是个抽象类,继承了IPlugin和 IssuePublisher.OnIssueDetectListener,IPlugin包括了五种插件的状态分别是CREATEINITEDSTARTEDSTOPPEDDESTROYED,当plugin状态发生变化时将回调交给pluginListener来处理。OnIssueDetectListener接口是IssuePublisher类里的内部接口,IssuePublisher具体做了两件事,记录问题和暴露问题,其暴露问题的方法就是空实现然后暴露接口,交给实现OnIssueDetectListener接口的具体类来处理,Plugin继承了这个OnIssueDetectListener接口,但它也没自己处理,也是同样交留pluginListener来处理。

第一段小结
  1. Matrix是个单例,它维护着插件的集合plugins和插件不同状态及报错的处理接口
  2. pluginListener,这个pluginListener是plugins集合共有的,
  3. matrix初始化的时候会逐个调用plugin 的init方法。
  4. 插件Plugin是个抽象类,具体的插件需要实现的,matrix框架里自带的插件有TracePluginIOCanaryPluginSQLiteLintPluginResourcePlugin
    下面会一一查看它们的作用和具体实现
TracePlugin

首先来看TracePlugin,它继承自plugin,里面包括四个维度FrameTracerFPSTracerEvilMethodTracerStartUpTracer来分析app的,初始化的方法如下:

 @Override
public void init(Application app, PluginListener listener) {
    super.init(app, listener);
    MatrixLog.i(TAG, "trace plugin init, trace config: %s", mTraceConfig.toString());
    //低版本不支持
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        MatrixLog.e(TAG, "[FrameBeat] API is low Build.VERSION_CODES.JELLY_BEAN(16), TracePlugin is not supported");
        unSupportPlugin();
        return;
    }
    ApplicationLifeObserver.init(app);
    mFrameTracer = new FrameTracer(this);
    //开关,可以选择不开
    if (mTraceConfig.isMethodTraceEnable()) {
        mStartUpTracer = new StartUpTracer(this, mTraceConfig);
    }
    if (mTraceConfig.isFPSEnable()) {
        mFPSTracer = new FPSTracer(this, mTraceConfig);
    }
    if (mTraceConfig.isMethodTraceEnable()) {
        mEvilMethodTracer = new EvilMethodTracer(this, mTraceConfig);
    }
}

ApplicationLifeObserver.init(app)是利用了application的ActivityLifecycleCallbacks可以对每个activity的生命周期进行监控做了个观察者模式,另外加了判断分析当前app是在前台还是后台,具体实现方式是记录onActivityResumed和onActivityPaused的生命周期,由于新起的activity的onResume会在底层activity的onPause之后,如果onActivityPaused之后600ms没有执行到onActivityResumed则认为当前处于后台。仔细想想这么做会有误伤,如果有个activity启动特别慢,此时超过600ms则判定已经处于后台了,不过这个影响比较小,因为activity启动之后到resume时就又恢复成正常的前台,即使误判也不影响检测,具体实现可以看源码。

在TracePlugin初始化的时候,分别新建了mStartUpTracer、mFPSTracer、mFrameTracer和mEvilMethodTracer,其中的参数mTraceConfig是个简单的配置类,只是记录了开关就不在这展开了。查看matrix的demo开始检测的入口是tracePlugin.start()里,代码如下:

@Override
public void start() {
    super.start();
    if (!isSupported()) {
        return;
    }
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //保证在主线程调用
            FrameBeat.getInstance().onCreate();
        }
    });
    if (null != mFPSTracer) {
        mFPSTracer.onCreate();
    }
    if (null != mEvilMethodTracer) {
        mEvilMethodTracer.onCreate();
    }
    if (null != mFrameTracer) {
        mFrameTracer.onCreate();
    }
    if (null != mStartUpTracer) {
        mStartUpTracer.onCreate();
    }
}

在onstart时在主线程中调用了FrameBeat.getInstance().onCreate(),这里是做UI分析用的。
目前做UI性能卡顿分析一般有两种方式:

一是利用主线程looper的loop方法在寻找msg.target.dispatchMessage(msg)时的前后会分别打印一段log,可以利用log的内容不同或者log的前后次数记录两次log的时间差,这样就可以大致认为是主线程处理msg的时间,如果时间过长则认为卡顿;

二是利用Choreographer,Choreographer就是一个消息处理器,根据vsync 信号 来计算frame,在doFrame方法里可以收到回调的当前时间,正常绘制两次doFrame的时间差应该是1000/60=16.6666毫秒(每秒60帧),但是遇到卡顿或过度重绘等会导致时间拉长。

这里采用的是第二种方式,其doFrame的实现如下:

 @Override
public void doFrame(long frameTimeNanos) {
    if (isPause) {
        return;
    }
    if (frameTimeNanos < mLastFrameNanos || mLastFrameNanos <= 0) {
        mLastFrameNanos = frameTimeNanos;
        if (null != mChoreographer) {
            mChoreographer.postFrameCallback(this);
        }
        return;
    }
    if (null != mFrameListeners) {
        for (IFrameBeatListener listener : mFrameListeners) {
            listener.doFrame(mLastFrameNanos, frameTimeNanos);
        }
        if (null != mChoreographer) {
            mChoreographer.postFrameCallback(this);
        }
        mLastFrameNanos = frameTimeNanos;
    }
}

记录两次doFrame的时间,交给mFrameListeners执行回调。
下面会分析mFPSTracer 、mFrameTracer、mFrameTracer和mStartUpTracer 的onCreate方法的具体实现,这四个类都继承了BaseTracer类,因此在分析得前先看下BaseTracer,

public abstract class BaseTracer extends IssuePublisher implements ApplicationLifeObserver.IObserver, IFrameBeatListener, IMethodBeatListener {

private final TracePlugin mPlugin;
private static final MethodBeat sMethodBeat = new MethodBeat();
private static final HashMap<Class<BaseTracer>, BaseTracer> sTracerMap =
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值