浅谈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-CanaryStartupTracer开头注释很容易理解

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,分析调用栈的耗时情况。


分析堆栈

  1. 调用TraceDataUtils.structuredDataToStack()方法
  2. 调用TraceDataUtils.trimStack()方法
  3. 调用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条,需要裁剪堆栈。裁剪策略如下:

  1. 从后往前遍历先序遍历结果,如果堆栈大小大于30,则将执行时间小于5*整体遍历次数的节点剔除掉

  2. 最多整体遍历60次,每次整体遍历,比较时间增加5ms

  3. 如果遍历了60次,堆栈大小还是大于30,将后面多余的删除掉


堆栈信息生成key
如果某个方法调用时间大于整个主线程调用时长的0.3倍,会将该方法id记录到list中,最后排序,过滤。生成traceKey。

根据裁剪后的先序遍历结果生成上报堆栈
reportBuilder就是最终上报的堆栈信息。与文章开头的日志信息一致

6.解析日志


日志解析结果如下:

三丶Matrix原理:资源格式

1.通用字段

  1. type:类型,用于区分同一个tag不同类型的上报
  2. tag: 该上报对应的tag
  3. stack:该上报对应的堆栈
  4. process:该上报对应的进程名
  5. time:issue 发生的时间

2.耗时上报

trace(包括启动、慢函数和 FPS 三种)

共同字段

  1. machine: 区分设备好坏的字段
  2. process: 进程

启动

  1. tag: Trace_StartUp
  2. scene: 对应的场景
  3. application_create:应用启动的耗时
  4. first_activity_create:activity 启动耗时
  5. stage_between_app_and_activity: 介于应用和 activity 两者之间的耗时
  6. splash_activity_duration,欢迎页耗时
  7. startup_duration 启动总耗时
  8. is_warm_start_up: 是否是软启动,值范围: true 和 false
  9. application_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文件在上传安装包的时候需要一起上传。 特殊字段如下:

  1. tag: Trace_EvilMethod
  2. detail,具体的耗时场景
    a. NORMAL, 普通慢函数场景
    b. ENTER, Activity进入场景
    c. ANR, anr超时场景
    d. FULL, 满buffer场景
    e. STARTUP, 启动耗时场景
  3. cost: 耗时
  4. stack: 堆栈
  5. stackKey: 客户端提取的 key,用来标识 issue 的唯一性

如果 detail == ENTER, 会增加viewInfo字段, 包括一下三个属性:

  1. viewDeep: view 的深度,是个整数
  2. viewCount: view 的数量,是个整数
  3. 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"

帧率

帧率这边需要统计整体帧率,已经按场景统计帧率情况

  1. tag: Trace_FPS
  2. scene:帧率对应的场景
  3. dropLevel:衡量帧率掉帧的水平
  4. dropSum:总共掉帧的总时长
  5. 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.内存泄漏上报

内存泄漏的具体信息如下:

  1. tag: memory
  2. resultZipPath:对应资源泄漏的hrpof文件,已经过裁剪。
  3. 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"

这里有两部分的异常:

  1. duplicated_bitmap: 内存出现重复的bitmap对象以及堆栈,这里会直接生成重复图片文件
  2. 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 当前存在四种类型的上报,的具体信息如下:

  1. tag: io

  2. type,耗时这边的类型有两种 a. MAIN_THREAD_IO=1, 在主线程IO超过200ms b. BUFFER_TOO_SMALL=2, 重复读取同一个文件,同一个堆栈超过3次 c. REPEAT_IO=3, 读写文件的buffer过小,即小于4k d. CLOSE_LEAK=4, 文件泄漏

  3. path: 文件的路径

  4. size: 文件的大小

  5. cost: 读写的耗时

  6. stack: 读写的堆栈

  7. op: 读写的次数

  8. buffer: 读写所用的buffer大小,要求大于4k

  9. thread: 线程名

  10. opType: 1为读,2为写

  11. opSize: 读写的总大小

  12. 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上报

具体信息如下:

  1. tag: SQLiteLint

  2. id: 用来标识 issue 类别的唯一性,例如:多个用户的 id 一样说明是同一个 issue

  3. type

typedef enum 
    kExplainQueryScanTable = 1, // 全表扫描,遍历数据表查找结果集,复杂度O(n)
    kExplainQueryUseTempTree, // 不必要的临时建树排序, 可用索引优化
    kExplainQueryTipsForLargerIndex, // 不足够的索引组合
    kAvoidAutoIncrement, // 使用了Autoincrement, 应避免使用
    kAvoidSelectAllChecker, // 使用了select *, 如非必要只需查询自己需要的列
    kWithoutRowIdBetter,	// 建议使用without rowid特性
    kPreparedStatementBetter,	// 建议使用prepared statement
    kRedundantIndex,	// 发现冗余索引
 IssueType;

  1. dbPath: 数据库路径

  2. level:

typedef enum 
    kPass = 0,
    kTips,
    kSuggestion,
    kWarning,
    kError,
 IssueLevel;
  1. table: 表名

  2. sql: 相关sql语句

  3. desc: 问题描述

  4. detail: 问题更详细的描述,如查询计划。

  5. advice: 优化建议

  6. createTime: 发现 issue 的时间

  7. stack: 该上报对应的堆栈

  8. isInMainThread: 是否在主线程

  9. 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使用原理的主要内容,如果未能解决你的问题,请参考以下文章

浅谈android中图片处理之图形变换特效Matrix

Android Matrix

Android TV按键焦点原理浅谈

浅谈Android 事件分发机制

Android IPC机制:浅谈Binder的使用

浅谈矩阵变换——Matrix