性能检测工具:Matrix-TraceCanary 入门
简介
TraceCanary分为帧率监控、慢方法监控、ANR监控、启动耗时、主线程优先级检测、IdleHandler耗时检测这6个功能。
基本使用
文档
TraceCanary 文档
Matrix GitHub
用法
1. 项目依赖
- 在项目根目录下的 gradle.properties中配置要依赖的Matrix版本号
MATRIX_VERSION=2.0.5
- 在项目根目录下的build.gradle文件添加Matrix依赖
dependencies {classpath ("com.tencent.matrix:matrix-gradle-plugin:${MATRIX_VERSION}") { changing = true }
}
- 在 app/build.gradle 文件中添加 Matrix 各模块的依赖
apply plugin: 'com.tencent.matrix-plugin'matrix {trace {enable = true //if you don't want to use trace canary, set falsebaseMethodMapFile = "${project.buildDir}/matrix_output/Debug.methodmap"blackListFile = "${project.projectDir}/matrixTrace/blackMethodList.txt"}
}dependencies {implementation group: "com.tencent.matrix", name: "matrix-android-lib", version: MATRIX_VERSION, changing: trueimplementation group: "com.tencent.matrix", name: "matrix-android-commons", version: MATRIX_VERSION, changing: trueimplementation group: "com.tencent.matrix", name: "matrix-trace-canary", version: MATRIX_VERSION, changing: true
}
2. 必要代码 (参考 Matrix中sample-android示例代码)
- 实现PluginListener,接收Matrix处理后的数据(参考TestPluginListener)
public class PluginListener extends DefaultPluginListener {public static final String TAG = "Matrix.PluginListener";public SoftReference<Context> softReference;private final Handler mHandler = new Handler(Looper.getMainLooper());public PluginListener(Context context) {super(context);softReference = new SoftReference<>(context);}@Overridepublic void onReportIssue(Issue issue) {super.onReportIssue(issue);MatrixLog.e(TAG, issue.toString());IssuesMap.put(IssueFilter.getCurrentFilter(), issue);jumpToIssueActivity();}public void jumpToIssueActivity() {Context context = softReference.get();Intent intent = new Intent(context, IssuesListActivity.class);if (context instanceof Application) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}context.startActivity(intent);}
}
TraceCanary项目本身是没有可视化页面,onReportIssue()方法会输出解析后数据。如果要想可视化效果,可以直接用sample-android中的IssuesListActivity类来集成到自己项目中。同时,还需要sample-android中issue包下IssueFilter、IssuesMap、ParseIssueUtil这些收集和解析数据功能的工具类。
- 实现动态配置接口,可修改Matrix内部参数(参考DynamicConfigImplDemo类)
public class DynamicConfigImpl implements IDynamicConfig {private static final String TAG = "Matrix.DynamicConfigImplDemo";public DynamicConfigImpl() {}public boolean isFPSEnable() {return true;}public boolean isTraceEnable() {return true;}public boolean isSignalAnrTraceEnable() {return true;}public boolean isMatrixEnable() {return true;}@Overridepublic String get(String key, String defStr) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.// for Activity leak detectif ((ExptEnum.clicfg_matrix_resource_detect_interval_millis.name().equals(key) || ExptEnum.clicfg_matrix_resource_detect_interval_millis_bg.name().equals(key))) {Log.d("DynamicConfig", "Matrix.ActivityRefWatcher: clicfg_matrix_resource_detect_interval_millis 10s");return String.valueOf(TimeUnit.SECONDS.toMillis(5));}if (ExptEnum.clicfg_matrix_resource_max_detect_times.name().equals(key)) {Log.d("DynamicConfig", "Matrix.ActivityRefWatcher: clicfg_matrix_resource_max_detect_times 5");return String.valueOf(3);}return defStr;}@Overridepublic int get(String key, int defInt) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.if (MatrixEnum.clicfg_matrix_resource_max_detect_times.name().equals(key)) {MatrixLog.i(TAG, "key:" + key + ", before change:" + defInt + ", after change, value:" + 2);return 2;//new value}if (MatrixEnum.clicfg_matrix_trace_fps_report_threshold.name().equals(key)) {return 10000;}if (MatrixEnum.clicfg_matrix_trace_fps_time_slice.name().equals(key)) {return 12000;}return defInt;}@Overridepublic long get(String key, long defLong) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.if (MatrixEnum.clicfg_matrix_trace_fps_report_threshold.name().equals(key)) {return 10000L;}if (MatrixEnum.clicfg_matrix_resource_detect_interval_millis.name().equals(key)) {MatrixLog.i(TAG, key + ", before change:" + defLong + ", after change, value:" + 2000);return 2000;}return defLong;}@Overridepublic boolean get(String key, boolean defBool) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.return defBool;}@Overridepublic float get(String key, float defFloat) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.return defFloat;}}
- 在Application的继承类中,对Matrix进行初始化(参考MatrixApplication)
public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// Switch.DynamicConfigImpl dynamicConfig = new DynamicConfigImpl();Matrix.Builder builder = new Matrix.Builder(this);TracePlugin tracePlugin = configureTracePlugin(dynamicConfig);builder.plugin(tracePlugin);Matrix.init(builder.build());tracePlugin.start();}private TracePlugin configureTracePlugin(DynamicConfigImpl dynamicConfig) {boolean fpsEnable = dynamicConfig.isFPSEnable();boolean traceEnable = dynamicConfig.isTraceEnable();boolean signalAnrTraceEnable = dynamicConfig.isSignalAnrTraceEnable();File traceFileDir = new File(getApplicationContext().getFilesDir(), "matrix_trace");if (!traceFileDir.exists()) {if (traceFileDir.mkdirs()) {MatrixLog.e(TAG, "failed to create traceFileDir");}}File anrTraceFile = new File(traceFileDir, "anr_trace"); File printTraceFile = new File(traceFileDir, "print_trace");TraceConfig traceConfig = new TraceConfig.Builder().dynamicConfig(dynamicConfig).enableFPS(fpsEnable).enableEvilMethodTrace(traceEnable).enableAnrTrace(traceEnable).enableStartup(traceEnable).enableIdleHandlerTrace(traceEnable) .enableMainThreadPriorityTrace(true) .enableSignalAnrTrace(signalAnrTraceEnable) .anrTracePath(anrTraceFile.getAbsolutePath()).printTracePath(printTraceFile.getAbsolutePath())// 换成自己项目启动页.splashActivities("sample.tencent.matrix.SplashActivity;").isDebug(true).isDevEnv(false).build();return new TracePlugin(traceConfig); }
}
至此,Matrix就已成功集成到你的项目中,并且开始收集和分析性能相关异常数据。
sample-android效果:
原理
TraceCanary 采用ASM插桩方案。插桩打点操作的类就是AppMethodBeat,该类会在编译期对函数的出入进行插桩i(methodId)/o(methodId),并在Activity#onWindowFocusChanged方法中插入at方法,供后面各种tracer使用。插桩的核心代码在插件中的MethodTracer。
插桩之外,我们还需要监控主线程MessageQueue(LooperMonitor)以及Choreographer(UIThreadMonitor),从里面抽象出一个可以通知Message开始执行、执行完成、Choreographer开始渲染的接口(LooperObserver)。基于这个接口,我们可以开始监控的实现。
FrameTracer(帧率监控)
在UIThreadMonitor中会通过LooperMonitor监听所有主线程的Message的执行,同时会向Choreographer#callbackQueues中插入一个回调来监听CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL这三种事务的执行耗时。
这样当Choreographer#FrameHandler开始执行的vsync时,UIThreadMonitor就可以捕获到vsync执行的起止时间,以及doFrame时各个部分的耗时。然后可以通过一系列计算来计算出帧率。实际上,只需要知道渲染每一帧的起止时间就可以算出帧率了。
// 具体代码见:sample-android 的TestFpsActivity类,下面给出关键代码public class TestFpsActivity extends Activity {// 帧率变化监听private IDoFrameListener mDoFrameListener = new IDoFrameListener(....){public void doFrameAsync(String focusedActivity, long startNs, long endNs, int dropFrame, boolean isVsyncFrame, long intendedFrameTimeNs, long inputCostNs, long animationCostNs, long traversalCostNs) {super.doFrameAsync(focusedActivity, startNs, endNs, dropFrame, isVsyncFrame, intendedFrameTimeNs, inputCostNs, animationCostNs, traversalCostNs);...}};protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);...IssueFilter.setCurrentFilter(IssueFilter.ISSUE_TRACE);// FrameTracer开始跟踪Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().onStartTrace();// 添加监听Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().addListener(mDoFrameListener);...} protected void onDestroy() {super.onDestroy();// 移除监听Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().removeListener(mDoFrameListener);// FrameTracer结束跟踪Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().onCloseTrace();}
}
结果:
字段含义:
- tag
Trace_FPS
- scene
帧率对应的场景
- dropLevel
衡量帧率掉帧的水平(次数)
- dropSum
总共掉帧的总时长
- fps
帧率
慢方法监控(EvilMethodTracer)
通过监控主线程中每个Message执行的起止时间,如果时间差超过一定的阈值,就认为发生了慢函数调用。此时可以通过AppMethodBeat中的数据,分析出这段时间内函数执行的堆栈信息,以及每个函数执行的耗时。这样,慢函数无所遁形了。
结果:
字段含义:
- tag
Trace_EvilMethod
- detail(具体的耗时场景)
NORMAL:代表普通慢函数场景。 ENTER:代表Activity进入场景。 ANR:anr超时场景。
FULL:代表满buffer场景。 STARTUP:启动耗时场景。
如果detail == ENTER,会增加viewInfo字段。包含3个属性:viewDeep:view的深度;viewCount :view的数量;activity :activity的name。
如果detail == STARTUP,会增加subType字段(默认值-1)。如果subType=1,代表application初始化过程的堆栈;如果subType=2,代表启动第一个界面初始化过程的堆栈。
- cost
耗时
- stack
堆栈
- stackKey
客户端提取的 key,用来标识issue的唯一性(方法ID)
打开app/build/outputs/mapping/debug/methodMapping.txt文件。从上图可知stackKey是118也就是方法id。
由上图可知,stackKey=118,对应就是TestEnterActivity类的onCreate 方法
ANR监控(AnrTracer)
在主线程中一般认为超过5s就会发生ANR。通过分析AppMethodBeat中的数据得到函数执行的堆栈以及耗时。
具体代码见:sample-android 的TestTraceMainActivity类public void testSignalANR(final View view) {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}
结果:
字段含义:
- tag
Trace_EvilMethod
- detail(具体的耗时场景)
ANR:anr超时场景
其他字段和慢方法监控字段一样。
在methodMapping.txt 文件中对应结果如下:
StartupTracer(启动耗时)
启动过程分析流程图:
firstMethod.i LAUNCH_ACTIVITY onWindowFocusChange LAUNCH_ACTIVITY onWindowFocusChange^ ^ ^ ^ ^| | | | |t0 t1 t2 t3|---------app---------|---|---firstActivity---|---------...---------|---careActivity---||<--applicationCost-->||<--------------firstScreenCost-------------->||<---------------------------------------coldCost------------------------------------->|. |<-----warmCost---->|
AppMethodBeat.i第一次发生时,可以认为是Application创建的时间(t0);第一次Activity的启动或者Service、Receiver的创建认为是Application创建的结束时间(t1)。这两者的时间差即为Application创建耗时。
第一个Activity的onWindowFocusChange发生时,即为时间t2,t2-t0就是首屏启动耗时。第二个Activity(一般是真正的MainActivity)的onWindowFocusChange发生时,即为时间t3,t3-t0就是冷启动的总耗时。
结果:
字段含义:
- tag
Trace_StartUp
- application_create
应用启动的耗时
- first_activity_create
activity 启动耗时
- startup_duration
启动总耗时
- is_warm_start_up
是否是软启动:true(是) false(不是)
- application_create_scene
100:代表activity拉起的。114:代表service拉起的。113:代表receiver拉起的。-100:代表未知的
性能检测工具:Matrix-TraceCanary 入门相关推荐
- 如何使用Lighthouse性能检测工具
前言 最近做性能检测工具,很多知识点不清楚,打算查缺补漏,补一补. 接下来从官方提供的性能检测工具Lighthouse(灯塔)开始我们的学习,简单介绍了下Lighthouse的一些点. 阅读完本文,你 ...
- coverity代码检测工具介绍_FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具...
FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具 摘要 性能测试的一个目标是找出某些特定情况,在这些情况下对于某些输入值组合,应用程序意外地展示出更糟糕的特性.性能测试的一个基本问题是如何 ...
- 在debug模式下引入一些性能检测工具
我们经常在debug模式下使用一些性能检测工具,例如blockCannary,leakCannary.Stetho等,但是我们release的时候又不需要这些检测工具,通常情况下我们的做法是在buil ...
- [Linux 性能检测工具]IOSTAT
[Linux 性能检测工具]IOSTAT IOSTAT NAME: Iostat, 报告CPU的统计,和 I/O的统计. 语法: iostat [ -c ] [ -d ] [ -N ...
- [Linux 性能检测工具]DF
[Linux 性能检测工具]DF DF NAME: df 报告了文件系统的使用 语法: df [OPTION]... [FILE]... 描述: Df显示了以文件名为从参数查看所在文件系统的可用空间. ...
- Unity官方性能检测工具UPR使用心得
Unity官方性能检测工具UPR使用心得 UPR是Unity官方推出的免费性能检测工具,近来功能较之前版本完善了许多,经笔者在正式项目中试用,与UWA的免费性能检测工具GPM作对比,发现UPR相较GP ...
- 季冠CPM云平台监控系统——自动化链路的性能检测工具
网络监测 自动化 智能化 在互联网高度发展的今天,我们每一个人都有过被网络支配的恐惧,除了手机没电,最害怕的就是网络卡顿和网络延迟. 尤其是对于线下实体门店.互联网公司.工厂及酒店等多分支网点企业来说 ...
- Unity性能优化之性能检测工具汇总
Unity Profiler 简述:Unity自带的性能分析工具,可以分析CPU.GPU以及内存消耗,支持真机调试. 官方教程:https://docs.unity3d.com/Manual/Prof ...
- java 性能检测工具 检测死锁等
为什么80%的码农都做不了架构师?>>> 死锁检测方法 1 JConsole 找到需要查看的进程,打开线程选项卡,点击检测死锁 2 jps查看java进程ID,使用jstack ...
最新文章
- python的编译器有哪些-python编译器有哪些
- java爬虫问题二: 使用jsoup爬取数据class选择器中空格多选择怎么解决
- 从蓝桥杯来谈Fibonacci数列
- Java源码研究之object to json string debug
- 拉格朗日乘数法_拉格朗日乘数法介绍(不含证明)
- Varnish 503错误
- 电机控制入门——学习路线规划以及学习书籍推荐
- 判断SuperView
- 易语言程序c0000005,教你四招彻底解决易语言程序误报问题
- l7sa008b故障代码_美国凯利冷机故障码表
- 拉格朗日插值和牛顿插值的龙格现象
- div 设置a4大小_javascript – 拉伸div到A4大小
- Edge 开发者日 · New Bing New Edge
- 【Linux】lftp客户端使用详解
- ucos移植到stm32上的中断小小改进
- digital_logic@一位全加器的真值表@画卡诺图@输出逻辑函数表达式
- Intellij IDEA Ultimate下载安装
- 二维码可以用哪款条码软件打印?
- 考试备战系列--软考--02基础知识复习
- AE文本复杂排列动画脚本TypeMonkey for mac
热门文章
- 如何发送和接收RTP封包的H264,用FFmpeg解码
- 视觉机器人+人体姿态识别项目总结
- 【评测】常规PCR(终点法,跑胶)的多重PCR的试剂
- 一场疫情,全民变厨子、医生变战士、教师变主播、只有孩子们,依然是神兽!...
- java js 高德api_地图-参考手册-地图 JS API | 高德地图API
- ESP32开发(1)----Espressif-IDE开发环境配置
- JEM software ticket45:Console output error of nQP when LCU level rate control is enabled
- python中的main函数可以被其他文件调用么_在Python中,如何在另一个py文件的[if\u name\uuuu='\uu main\uu']中调用子例程?...
- 两个向量夹角的cos值
- 基于微信教室预约小程序系统设计与实现 开题报告