写在前面
近期开始 Android Framework 层的学习,然而较为庞大的 Framework 让人感觉无从下手。碰巧看到一篇文章说到腾讯的 性能监控框架 Matrix 用到了大量 Framework 相关的知识,所以试着分析下该框架的源码实现。
在学习大佬们代码的同时主要关注该框架用到了哪些、是怎么使用的 Framework 的内容。
一、Matrix 简介
官方说明
Matrix 是一款微信研发并日常使用的应用性能接入框架,支持iOS, macOS和Android。 Matrix 通过接入各种性能监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。
大公司就是大气,直接双端都给你整一套。
Matrix 地址
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 监控
好家伙,功能还真不少。看样子是个大工程,排个计划吧:
- 首先是对框架的大致了解,对框架中用到的需要用到的类、函数进行预习;
- 从某一模块入手,分析功能实现的同时注重 Framework 的内容;
- 最后进行总结,思考为什么这样做,有没有更好的做法。
那么本文先大概了解一下框架,遇到 Framework 中的知识进行简单的了解和预习。
二、使用 Matrix
有关 Matrix 的接入和使用官方文档已经写得很清楚了,本文简单总结下:
- 引入 Matrix 库,添加相关依赖;
- 创建插件监听,可以接收到插件的启动和工作通知。
Matrix 的功能基本都是由这些 插件 Plugin 实现的,这样做的好处一方面是解耦,另一方面是用户可以根据需要选择使用的功能。 - 在 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);
}
}
- 枚举实现单例的原理是利用枚举的特点来实现的:枚举类型是线程安全的,并且只会装载一次。
- HandlerTherad 继承了 Thread,可以看作是一个线程。而其内部维护了一个 Handler,在调用 start() 方法后初始化 Looper,可以很方便地执行异步任务。其它类可以通过
getThreadHandler
方法获取 HandlerTherad 的 Handler,然后 post 任务由 HandlerTherad 内部的 Looper 取出并执行。 - registerComponentCallbacks:Application 的方法,作用是监听应用的内存状态。
在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用 onLowMemory。
OnTrimMemory 是 Android 4.0 之后提供的 API。比起 onLowMemory,这个回调新增返回了一个 int 值表示当前内存状态,开发者可以根据返回的状态来适当回收资源避免 app 被杀死的风险。
想要监听内存状态回调需要实现 ComponentCallbacks2 接口,该接口是 ComponentCallbacks 的升级版。
应用内存优化之OnLowMemory&OnTrimMemory
- 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
整个过程就是利用反射操作 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
@Override
public 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,做后续工作
}
}
总结
画个简单的流程图:
到这里,Matrix 大致的工作流程已经搞清楚了。但是到现在基本没有接触核心功能,Matrix 是怎么分析卡顿的?怎么分析 ANR 的?... 后面会发文继续分析,敬请期待。
系列文章
Android 腾讯 Matrix 原理分析(二):TracePlugin 卡顿分析之主线程监听
Android 腾讯 Matrix 原理分析(三):TracePlugin 卡顿分析之帧率监听
Android 腾讯 Matrix 原理分析(四):TracePlugin 卡顿分析之丢帧展现