浅谈Android Matrix使用原理
Posted 初一十五啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈Android Matrix使用原理相关的知识,希望对你有一定的参考价值。
前言
看了一下关于对android性能监控框架Matrix的介绍九个模块的内容,已经有jym阐述过,就不对赘述了,找不到的再说.🤣
分别为:
Matrix
介绍- 内存泄漏监控及原理介绍
- 内存泄漏监控源码分析
Hprof
文件分析- 卡顿监控
- 卡顿监控源码解析
- 插桩和资源优化
- I/O监控及原理解析
今天来说一下关于Matrix的搭建,流程,资源格式🤨
关注公众号:初一十五a
解锁 《Android十大板块文档》,让学习更贴近未来实战。已形成PDF版
内容如下:
1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试应有尽有
3.Android车载应用大合集,从零开始一起学
4.性能优化大合集,告别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对工作需求
10.Android基础篇大合集,根基稳固高楼平地起
整理不易,关注一下吧。开始进入正题,ღ( ´・ᴗ・` ) 🤔
一丶Matrix原理:Matrix搭建
一、接入步骤
1. 项目的根目录下的gradle.properties
文件声明接入Matrix的版本
lib_thirds_matrix_version=2.0.8
2. 项目的根目录下的build.gradle
文件 声明Matrix Gradle 插件
buildscript
repositories
google()
mavenCentral()
dependencies
classpath "com.android.tools.build:gradle:4.2.2"
classpath ("com.tencent.matrix:matrix-gradle-plugin:$lib_thirds_matrix_version") changing = true
3. 在app build.gralde
文件声明apply Matrix插件
apply plugin: 'com.tencent.matrix-plugin'
matrix
trace
enable = true //if you don't want to use trace canary, set false
baseMethodMapFile = "$project.buildDir/matrix_output/Debug.methodmap"
blackListFile = "$project.projectDir/matrixTrace/blackMethodList.txt"
4. 在项目接入Matrix的Module 进行依赖
一般我们是在app module下的build.gradle
进行依赖,但是笔者比较喜欢按功能划分module,所以新建一个Module: apmlib, 并且在apmlib的build.gradle
下进行依赖。
dependencies
//按需依赖需要的canary
implementation group: "com.tencent.matrix", name: "matrix-android-lib", version: lib_thirds_matrix_version, changing: true
implementation group: "com.tencent.matrix", name: "matrix-android-commons", version: lib_thirds_matrix_version, changing: true
implementation group: "com.tencent.matrix", name: "matrix-trace-canary", version: lib_thirds_matrix_version, changing: true
implementation group: "com.tencent.matrix", name: "matrix-io-canary", version: lib_thirds_matrix_version, changing: true
// implementation group: "com.tencent.matrix", name: "matrix-memory-canary", version: lib_thirds_matrix_version, changing: true
// implementation group: "com.tencent.matrix", name: "matrix-resource-canary-android", version: lib_thirds_matrix_version, changing: true
// implementation group: "com.tencent.matrix", name: "matrix-resource-canary-common", version: lib_thirds_matrix_version, changing: true
// implementation group: "com.tencent.matrix", name: "matrix-sqlite-lint-android-sdk", version: lib_thirds_matrix_version, changing: true
// implementation group: "com.tencent.matrix", name: "matrix-battery-canary", version: lib_thirds_matrix_version, changing: true
// implementation group: "com.tencent.matrix", name: "matrix-hooks", version: lib_thirds_matrix_version, changing: true
// implementation group: "com.tencent.matrix", name: "matrix-backtrace", version: lib_thirds_matrix_version, changing: true
......
5. 自定义一个单例管理器来完成Matrix的初始化工作
package com.mikel.apmlib;
import android.app.Application;
import com.tencent.matrix.Matrix;
import com.tencent.matrix.iocanary.IOCanaryPlugin;
import com.tencent.matrix.iocanary.config.IOConfig;
import com.tencent.matrix.trace.TracePlugin;
import com.tencent.matrix.trace.config.TraceConfig;
public class MatrixManager
private static volatile MatrixManager INSTANCE;
private MatrixManager()
public static MatrixManager getInstance()
if (INSTANCE == null)
synchronized (MatrixManager.class)
if (INSTANCE == null)
INSTANCE = new MatrixManager();
return INSTANCE;
public void doOnCreate(Application application, String splashActivity)
Matrix.Builder builder = new Matrix.Builder(application); // build matrix
builder.pluginListener(new MatrixPluginListener(application)); // add general pluginListener
MatrixDynamicConfigImpl matrixDynamicConfig = new MatrixDynamicConfigImpl(); // dynamic config
boolean fpsEnable = matrixDynamicConfig.isFPSEnable();
boolean traceEnable = matrixDynamicConfig.isTraceEnable();
//Trace plugin
TraceConfig traceConfig = new TraceConfig.Builder()
.dynamicConfig(matrixDynamicConfig)
.enableFPS(fpsEnable)//帧率
.enableEvilMethodTrace(traceEnable)//慢方法
.enableAnrTrace(traceEnable)//anr
.enableStartup(traceEnable)//启动速度
.splashActivities(splashActivity)//首页
//debug模式
.isDebug(true)
//dev环境
.isDevEnv(false)
.build();
TracePlugin tracePlugin = new TracePlugin(traceConfig);
builder.plugin(tracePlugin);
// io plugin
IOCanaryPlugin ioCanaryPlugin = new IOCanaryPlugin(new IOConfig.Builder()
.dynamicConfig(matrixDynamicConfig)
.build());
builder.plugin(ioCanaryPlugin);
//init matrix
Matrix.init(builder.build());
tracePlugin.start();
ioCanaryPlugin.start();
本文只使用了Matrix的TraceCanary和IOCanary来做Demo, Matrix的功能远不止这些,还有其他的Canary,可以按需接入。
此外,初始化的时候,可以继承Matrix的DefaultPluginListener
进行性能监控的数据监听以及实现IDynamicConfig
自定义一些性能监控的阈值。
package com.mikel.apmlib;
import android.content.Context;
import com.tencent.matrix.plugin.DefaultPluginListener;
import com.tencent.matrix.report.Issue;
import com.tencent.matrix.util.MatrixLog;
public class MatrixPluginListener extends DefaultPluginListener
public static final String TAG = "MatrixPluginListener";
public MatrixPluginListener(Context context)
super(context);
@Override
public void onReportIssue(Issue issue)
super.onReportIssue(issue);
//todo 处理性能监控数据
MatrixLog.e(TAG, issue.toString());
package com.mikel.apmlib;
import com.tencent.mrs.plugin.IDynamicConfig;
public class MatrixDynamicConfigImpl implements IDynamicConfig
public MatrixDynamicConfigImpl()
public boolean isFPSEnable() return true;
public boolean isTraceEnable() return true;
public boolean isMatrixEnable() return true;
public boolean isDumphprof() return false;
@Override
public String get(String key, String defStr)
//hook to change default values
return defStr;
@Override
public int get(String key, int defInt)
//hook to change default values
return defInt;
@Override
public long get(String key, long defLong)
//hook to change default values
// if (IDynamicConfig.ExptEnum.clicfg_matrix_trace_evil_method_threshold.name().equals(key))
// return 300; // 默认为700
//
return defLong;
@Override
public boolean get(String key, boolean defBool)
//hook to change default values
return defBool;
@Override
public float get(String key, float defFloat)
//hook to change default values
return defFloat;
6. 在Application.onCreate方法调用Matrix初始化
MatrixManager.getInstance().doOnCreate(getApplication(),"com.mikel.projectdemo.MainActivity");
二、 Matrix 性能数据分析
1. 慢方法监控
Matrix 对堆栈进行了聚合,解决堆栈数据量大,后台很难聚类的问题,详细原理见:
Matrix Android TraceCanary · Tencent/matrix Wiki · GitHub
根据TraceStack 的 methidId,在项目app-build-outputs-mapping-debug-methodMapping.txt 找到对应方法。
2. ANR监控
3. 启动速度监控
启动速度监控根据Matrix Trace-Canary
的StartupTracer
开头注释很容易理解
application_create
: 第一个方法调用时 到 四大组件创建时 对应下图applicationCost
first_activity_create
: 第一个方法调用时 到 首个Activity.onWindowFocusChange
对应下图firstScreenCost
冷启动:第一个方法调用时 到 MainActivity.onWindowFocusChange
对应下图coldCost
温启动:首个Activity.onCreate
到 首个Activity.onWindowFocusChange
对应下图warmCost
4. 帧率监控
过滤TAG :Trace_FPS
可以得到帧率监控数据
关键字段解析:
scene
: 关键页面
dropLevel
: 一个采样周期,根据每60帧的丢帧数落入不同区间的次数。
DROPPED_FROZEN
代表采样周期内每60帧丢了42帧以上的次数
DROPPED_HIGH
代表采样周期内每60帧丢了24-42帧的次数
DROPPED_MIDDLE
代表采样周期内每60帧里丢了9-24帧的次数
DROPPED_NONAL
代表采样周期内每60帧里丢了3-9帧的次数
DROPPED_BEST
代表采样周期内每60帧丢了0-3帧的次数
dropSum
: 一个采样周期,落入不同区间的累计丢帧数。
DROPPED_FROZEN
代表采样周期内每60帧丢了42帧以上的累计丢帧数
DROPPED_HIGH
代表采样周期内每60帧丢了24-42帧的累计丢帧数
DROPPED_MIDDLE
代表采样周期内每60帧里丢了9-24帧的累计丢帧数
DROPPED_NONAL
代表采样周期内每60帧里丢了3-9帧的累计丢帧数
DROPPED_BEST
代表采样周期内每60帧丢了0-3帧的累计丢帧数
fps: 帧率, 监控指标
二丶Matrix原理:Matrix流程
1.Matrix功能简介
Matrix 当前监控范围包括:「应用安装包大小,SQLite 操作优化,帧率变化,卡顿,启动耗时,页面切换耗时,慢方法,文件读写性能,I/O 句柄泄漏, 内存泄漏等。」
2.满方法演示
官方Demo,TestTraceMainActivity#testJankiess(View view)模拟在主线程调用方法超过700ms的场景。Matrix中慢方法的默认阈值是700ms。用户可配置。对应的字段是
//Constants.java
public static final int DEFAULT_EVIL_METHOD_THRESHOLD_MS = 700;
点击EVIL_METHOD
按钮,会调用testJankiess
方法。打印Log如下
乍一看,有点丈二的和尚摸不着头脑。出现这样的日志,说明主线程调用时长超过了700ms。把日志中content对应的json格式化,得到如下结果:
"machine":"UN_KNOW", "cpu_app":0, "mem":1567367168, "mem_free":851992, "detail":"NORMAL", "cost":2262, "usage":"0.35%", "scene":"sample.tencent.matrix.trace.TestTraceMainActivity", "stack":"0,1048574,1,2262\\n 1,117,1,2254\\n 2,121,1,2254\\n 3,124,1,384\\n 4,125,1,165\\n 5,126,1,21\\n 5,127,1,21\\n 5,128,1,19\\n 4,129,1,24\\n 3,130,1,65\\n 4,131,1,21\\n 4,132,1,6\\n 4,133,1,8\\n 3,134,1,1004\\n", "stackKey":"121|", "tag":"Trace_EvilMethod", "process":"sample.tencent.matrix", "time":1620523013050
关于数据格式,官方也有一篇文章介绍,
Matrix Android data format · Tencent/matrix Wiki · GitHub,感兴趣的同学可以去看看。
本文重点关注stack字段。它的功能是上报对应的堆栈。但是堆栈中为啥是一堆阿拉伯数字呢?先让我们从头说起了。
3.计算方法调用的时间花费
3.1. 计算一个方法调用花费的时间
假设有方法A。我想计算它花费的时间。我们一般会这样做
public void A()
long startTime = SystemClock.uptimeMillis()
SystemClock.sleep(1000);
long endTime = SystemClock.uptimeMillis()
System.out.println("cost time " + (endTime-startTime));
对于单个方法我们可以这样做。「但是如果我想给Android项目中所有的方法都计算调用花费时,我们需要用到字节码插桩技术。在所有的方法开始处和结束处,添加记录时间的代码。而Matrix也正是使用插桩技术来计算方法的时间调用的。」
项目工程中TestTraceMainActivity
的A方法
使用Jadx工具反编译apk中的TestTraceMainActivity
。发现A方法前后增加了**「AppMethoBeat.i(121)和AppMethoBeat.o(121)」**
「i/o 方法参数121是什么意思呢?」
3.2 121含义讲解
gradle插件,在处理方法时,会将方法名与从1开始递增的数字对应起来。「我们打开app/build/outputs/mapping/debug/methodMapping.txt文件。从图片我们可以看到121对应的方法名是sample.tencent.matrix.trace.TestTraceMainActivity A ()V」
前文堆栈中的数字0,1048574,1,2262\\n 第二列1048574对应的就是方法名对应的数字。「这么做的好处是,数据采集节省流量。」
3.3.获取调用栈的耗时
有方法调用如下,假设A方法调用耗时1000ms。如何能够确定调用栈中哪个子方法的调用最耗时?
public void A()
B(); C(); D();
Matrix框架已经实现了调用栈耗时监测,具体分析我放到后面讲解。「重点就是后文5.2章节」
3.4.获取主线程耗时
依赖主线程Looper,监控每次dispatchMessage
的执行耗时
public static void loop()
...
for (;;)
...
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null)
logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
msg.target.dispatchMessage(msg);
if (logging != null)
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
...
主线程所有执行的任务都在 dispatchMessage
方法中派发执行完成,「我们通过 setMessageLogging
的方式给主线程的 Looper 设置一个 Printer ,因为 dispatchMessage
执行前后都会打印对应信息。我们可以计算出执行前后的时间花费。」
4.Matrix如何实现插桩
本文只是简单介绍Matrix的插桩技术。浅尝辄止。实现插桩的代码是com/tencent/matrix/trace/MethodTracer.java
,「它的内部类TraceMethodAdapter
实现了AppMethoBeat.i()
和AppMethoBeat.o()
的插入功能。」
5.慢方法监测的原理
5.1.手画调用栈树
第二节提到的慢方法演示,它的代码调用如下
public void testJankiess(View view)
A();
private void A()
B(); H(); L(); SystemClock.sleep(800);
private void B()
C(); G(); SystemClock.sleep(200);
private void C()
D(); E(); F(); SystemClock.sleep(100);
private void D()
SystemClock.sleep(20);
private void E()
SystemClock.sleep(20);
private void F()
SystemClock.sleep(20);
private void G()
SystemClock.sleep(20);
private void H()
SystemClock.sleep(20); I(); J(); K();
private void I()
SystemClock.sleep(20);
private void J()
SystemClock.sleep(6);
private void K()
SystemClock.sleep(10);
private void L()
SystemClock.sleep(1000);
「它对应的methodMapping
文件如下:」
117,1,sample.tencent.matrix.trace.TestTraceMainActivity testJankiess (Landroid.view.View;)V
121,2,sample.tencent.matrix.trace.TestTraceMainActivity A ()V
122,4,sample.tencent.matrix.battery.TestBatteryActivity onDestroy ()V
123,9,sample.tencent.matrix.sqlitelint.TestSQLiteLintHelper qualityClose (Ljava.io.Closeable;)V
124,2,sample.tencent.matrix.trace.TestTraceMainActivity B ()V
125,2,sample.tencent.matrix.trace.TestTraceMainActivity C ()V
126,2,sample.tencent.matrix.trace.TestTraceMainActivity D ()V
127,2,sample.tencent.matrix.trace.TestTraceMainActivity E ()V
128,2,sample.tencent.matrix.trace.TestTraceMainActivity F ()V
129,2,sample.tencent.matrix.trace.TestTraceMainActivity G ()V
130,2,sample.tencent.matrix.trace.TestTraceMainActivity H ()V
131,2,sample.tencent.matrix.trace.TestTraceMainActivity I ()V
132,2,sample.tencent.matrix.trace.TestTraceMainActivity J ()V
133,2,sample.tencent.matrix.trace.TestTraceMainActivity K ()V
134,2,sample.tencent.matrix.trace.TestTraceMainActivity L ()V
1048574,1,android.os.Handler dispatchMessage (Landroid.os.Message;)V
「以上方法调用可以归纳成以下树形结构:」
5.2.生成调用栈树
函数调用记录在队列中
编译期已经对全局的函数进行插桩,在运行期间每个函数的执行前后都会调用 MethodBeat.i/o
的方法,如果是在主线程中执行,则在函数的执行前后获取当前距离 MethodBeat 模块初始化的时间 offset 「(为了压缩数据,存进一个long类型变量中),」 并将当前执行的是 MethodBeat i或者o、mehtod id 及时间 offset,存放到一个 long 类型变量中,记录到一个预先初始化好的数组 long[] 中 index 的位置(预先分配记录数据的 buffer 长度为 100w,内存占用约 7.6M)。数据存储如下图:
//AppMethodBeat.java
private static long[] sBuffer = new long[Constants.BUFFER_SIZE];
//Constants.java
public static final int BUFFER_SIZE = 100 * 10000; // 7.6M
「AppMethodBeat.i/o
方法最终会调用到」
//AppMethodBeat.java
private static void mergeData(int methodId, int index, boolean isIn)
if (methodId == AppMethodBeat.METHOD_ID_DISPATCH)
sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
long trueId = 0L;
if (isIn)
trueId |= 1L << 63;
trueId |= (long) methodId << 43;
trueId |= sCurrentDiffTime & 0x7FFFFFFFFFFL;
sBuffer[index] = trueId;
checkPileup(index);
sLastIndex = index;
「testJankiess
方法调用,通过mergeData
方法,最终填充sBuffer如下图:」
主线程调用结束后判断是否超过阈值
EvilMethodTracer.java dispatchEnd
表示主线程执行结束,如果耗时超过阈值,会在MatrixHandlerThread
中执行AnalyseTask
,分析调用栈的耗时情况。
分析堆栈
- 调用
TraceDataUtils.structuredDataToStack()
方法- 调用
TraceDataUtils.trimStack()
方法- 调用
TraceDataUtils.getTreeKey()
方法
将调用队列转换成树的先序遍历结果
- 调用
addMethodItem
方法,将后序遍历结果push到栈中
structuredDataToStack()
中,如果是out方法,将会出栈,并且push到栈底。得到结果如下:
如果我们将队列反转过来,对照手画的树我们可知结果是**「后序遍历。」**
126 127 128 125 129 124 131 132 133 130 134 121 117 1048574
- 调用
stackToTree()
将队列转换成多叉树
结果和手画的树一样
- 调用
treeToStack()
,获得先序遍历结果
结果如下:
裁剪调用堆栈
Matrix默认最多上传30个堆栈。如果堆栈调用超过30条,需要裁剪堆栈。裁剪策略如下:
从后往前遍历先序遍历结果,如果堆栈大小大于30,则将执行时间小于5*整体遍历次数的节点剔除掉
最多整体遍历60次,每次整体遍历,比较时间增加5ms
如果遍历了60次,堆栈大小还是大于30,将后面多余的删除掉
堆栈信息生成key
如果某个方法调用时间大于整个主线程调用时长的0.3倍,会将该方法id记录到list中,最后排序,过滤。生成traceKey。
根据裁剪后的先序遍历结果生成上报堆栈
reportBuilder
就是最终上报的堆栈信息。与文章开头的日志信息一致
6.解析日志
日志解析结果如下:
三丶Matrix原理:资源格式
1.通用字段
- type:类型,用于区分同一个tag不同类型的上报
- tag: 该上报对应的tag
- stack:该上报对应的堆栈
- process:该上报对应的进程名
- time:issue 发生的时间
2.耗时上报
trace(包括启动、慢函数和 FPS 三种)
共同字段:
- machine: 区分设备好坏的字段
- process: 进程
启动:
tag
:Trace_StartUp
scene
: 对应的场景application_create
:应用启动的耗时first_activity_create
:activity 启动耗时stage_between_app_and_activity
: 介于应用和 activity 两者之间的耗时splash_activity_duration
,欢迎页耗时startup_duration
启动总耗时is_warm_start_up
: 是否是软启动,值范围: true 和 falseapplication_create_scene
:启动的场景
- 100 (activity拉起的)
- 114(service拉起的)
- 113 (receiver拉起的)
- -100 (未知,比如contentprovider)
"machine": 4,
"application_create": 415,
"first_activity_create": 240,
"stage_between_app_and_activity": 0,
"scene": "com.tencent.mm.app.WeChatSplashActivity",
"is_warm_start_up": false,
"tag": "Trace_StartUp",
"process": "com.tencent.mm",
"time": 1528278018147
慢函数:
对于stack需要通过method_mapping
文件解析堆栈,mapping文件在上传安装包的时候需要一起上传。 特殊字段如下:
tag
:Trace_EvilMethod
detail
,具体的耗时场景
a. NORMAL, 普通慢函数场景
b. ENTER, Activity进入场景
c. ANR, anr超时场景
d. FULL, 满buffer场景
e. STARTUP, 启动耗时场景cost
: 耗时stack
: 堆栈stackKey
: 客户端提取的 key,用来标识 issue 的唯一性
如果 detail == ENTER
, 会增加viewInfo字段, 包括一下三个属性:
- viewDeep: view 的深度,是个整数
- viewCount: view 的数量,是个整数
- activity: activity 的 name
如果 detail == STARTUP
, 会增加subType 字段,默认值-1
- subType=1,代表application初始化过程的堆栈
- subType=2,代表启动第一个界面初始化过程的堆栈
"machine": 2015,
"detail": "ENTER",
"cost": 3205,
"viewInfo":
"viewDeep": 10,
"viewCount": 6,
"activity": "TestFpsActivity"
,
"stack": "3,195,1,10\\n1,33,1,58\\n2,206,1,21\\n3,161,1,16\\n4,180,1,16\\n5,169,1,16\\n6,96,1,10\\n7,98,1,10\\n4,183,2,5\\n5,211,6,0\\n0,30,1,56\\n",
"stackKey": "0,30,1,56\\n",
"tag": "Trace_EvilMethod",
"process": "sample.tencent.matrix"
帧率
帧率这边需要统计整体帧率,已经按场景统计帧率情况
tag
:Trace_FPS
scene
:帧率对应的场景dropLevel
:衡量帧率掉帧的水平dropSum
:总共掉帧的总时长fps
: 帧率
"machine": 2015,
"scene": "sample.tencent.matrix.trace.TestFpsActivity",
"dropLevel":
"DROPPED_HIGH": 4,
"DROPPED_MIDDLE": 12,
"DROPPED_NORMAL": 18,
"DROPPED_BEST": 113
,
"dropSum":
"DROPPED_HIGH": 60,
"DROPPED_MIDDLE": 96,
"DROPPED_NORMAL": 51,
"DROPPED_BEST": 6
,
"fps": 24.476625442504883,
"tag": "Trace_FPS",
"process": "sample.tencent.matrix"
3.内存泄漏上报
内存泄漏的具体信息如下:
- tag: memory
- resultZipPath:对应资源泄漏的hrpof文件,已经过裁剪。
- activity:泄漏的Activity名称
事例:
内存泄漏这里我们通过文件上传方式,事例如下:
"resultZipPath": "\\/storage\\/emulated\\/0\\/Android\\/data\\/com.tencent.mm\\/cache\\/matrix_resource\\/dump_result_17400_20170713183615.zip",
"activity": "com.tencent.mm.plugin.setting.ui.setting.SettingsUI",
"tag": "memory",
"process": "com.tencent.mm"
这里有两部分的异常:
duplicated_bitmap
: 内存出现重复的bitmap对象以及堆栈,这里会直接生成重复图片文件leaked_activities
:泄漏的Activity以及堆栈
考虑到性能,除了上传 Hprof 文件,我们还有轻量级的上报(不需要 dump Hprof 文件),走数据上报通道,所以除了需要解析 Hprof 文件,还需要从数据通道那里解析数据:
"activity":"com.tencent.mm.plugin.setting.ui.setting.SettingsUI","tag":"memory","process":"com.tencent.mm"
4.I/O上报
IO 当前存在四种类型的上报,的具体信息如下:
-
tag: io
-
type,耗时这边的类型有两种 a. MAIN_THREAD_IO=1, 在主线程IO超过200ms b. BUFFER_TOO_SMALL=2, 重复读取同一个文件,同一个堆栈超过3次 c. REPEAT_IO=3, 读写文件的buffer过小,即小于4k d. CLOSE_LEAK=4, 文件泄漏
-
path: 文件的路径
-
size: 文件的大小
-
cost: 读写的耗时
-
stack: 读写的堆栈
-
op: 读写的次数
-
buffer: 读写所用的buffer大小,要求大于4k
-
thread: 线程名
-
opType: 1为读,2为写
-
opSize: 读写的总大小
-
repeat:
a. REPEAT_IO : 重复的次数
b. Main_IO:1 - 单次操作 2 - 连续读写 3 -2种行为
I/O事例
IO的字段基本一样,下面是重复IO的一个事例。
"path": "\\/data\\/user\\/0\\/com.tencent.mm\\/MicroMsg\\/MM_stepcounter.cfg",
"size": 496,
"op": 60,
"buffer": 289,
"cost": 7,
"opType": "r",
"opSize": 496,
"thread": "MM_Thread_Pool_Free_Handler_Thread#3#initThread",
"stack": "java.io.FileInputStream.<init>(FileInputStream.java:76)\\ncom.tencent.mm.storage.ConfigFileStorage.openCfg(ConfigFileStorage.java:78)\\ncom.tencent.mm.storage.ConfigFileStorage.<init>(ConfigFileStorage.java:30)\\ncom.tencent.mm.plugin.sport.model.SportFileStorage.<init>(SportFileStorage.java:12)\\ncom.tencent.mm.plugin.sport.model.SportFileStorageLogic.create(SportFileStorageLogic.java:21)\\ncom.tencent.mm.plugin.sport.PluginSport.execute(PluginSport.java:44)\\ncom.tencent.mm.kernel.boot.Boot.executeTask(Boot.java:111)\\ncom.tencent.mm.kernel.boot.Boot$1.call(Boot.java:71)\\n",
"tag": "io",
"type": 3,
"process": "com.tencent.mm"
资源泄漏的上报会不太一样,具体如下:
"stack":"dalvik.system.CloseGuard.open(CloseGuard.java:180)\\njava.io.RandomAccessFile.
<init>(RandomAccessFile.java:127)\\ncom.tencent.smtt.utils.DataReader.<init>(Unknown
Source)\\ncom.tencent.smtt.utils.DataReader.<init>(Unknown
Source)\\ncom.tencent.smtt.sdk.OatHelper.getOatCommand(Unknown
Source)\\ncom.tencent.smtt.sdk.TbsInstaller.doDexoatForArtVm(Unknown
Source)\\ncom.tencent.smtt.sdk.TbsInstaller.doDexoptOrDexoat(Unknown
Source)\\ncom.tencent.smtt.sdk.TbsInstaller.installTbsCoreInThread(Unknown
Source)\\n","tag":"io","type":4,"process":"com.tencent.mm:sandbox"
5.SQLiteLint上报
具体信息如下:
-
tag: SQLiteLint
-
id: 用来标识 issue 类别的唯一性,例如:多个用户的 id 一样说明是同一个 issue
-
type
typedef enum
kExplainQueryScanTable = 1, // 全表扫描,遍历数据表查找结果集,复杂度O(n)
kExplainQueryUseTempTree, // 不必要的临时建树排序, 可用索引优化
kExplainQueryTipsForLargerIndex, // 不足够的索引组合
kAvoidAutoIncrement, // 使用了Autoincrement, 应避免使用
kAvoidSelectAllChecker, // 使用了select *, 如非必要只需查询自己需要的列
kWithoutRowIdBetter, // 建议使用without rowid特性
kPreparedStatementBetter, // 建议使用prepared statement
kRedundantIndex, // 发现冗余索引
IssueType;
-
dbPath: 数据库路径
-
level:
typedef enum
kPass = 0,
kTips,
kSuggestion,
kWarning,
kError,
IssueLevel;
-
table: 表名
-
sql: 相关sql语句
-
desc: 问题描述
-
detail: 问题更详细的描述,如查询计划。
-
advice: 优化建议
-
createTime: 发现 issue 的时间
-
stack: 该上报对应的堆栈
-
isInMainThread: 是否在主线程
-
sqlTimeCost: sql 执行耗时,单位 ms.
注意: 只有 kExplainQueryScanTable kExplainQueryUseTempTree kExplainQueryTipsForLargerIndex kAvoidSelectAllChecker 这几种此字段才有效
关注公众号:初一十五a
解锁 《Android十大板块文档》,让学习更贴近未来实战。已形成PDF版
内容如下:
1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试应有尽有
3.Android车载应用大合集,从零开始一起学
4.性能优化大合集,告别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对工作需求
10.Android基础篇大合集,根基稳固高楼平地起
整理不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔
以上是关于浅谈Android Matrix使用原理的主要内容,如果未能解决你的问题,请参考以下文章