性能优化一分钟定位Android启动耗时问题

简介: 性能优化一分钟定位Android启动耗时问题

前言


Tencent Matrix默认无法监测Application冷启动的耗时方法,本文介绍了如何改造Matrix支持冷启动耗时方法监测。让你一分钟就能给App启动卡顿号脉。


1. 接入Tencent Matrix


1.1 在你项目根目录下的 gradle.properties 中配置要依赖的 Matrix 版本号,如:


MATRIX_VERSION=1.0.0

1.2 在你项目根目录下的 build.gradle 文件添加 Matrix 依赖,如:

dependencies {
      classpath ("com.tencent.matrix:matrix-gradle-plugin:${MATRIX_VERSION}") { changing = true }
  }

1.3 在 app/build.gradle 文件中添加 Matrix 各模块的依赖,如:

dependencies {
    implementation group: "com.tencent.matrix", name: "matrix-android-lib", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-android-commons", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-trace-canary", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-resource-canary-android", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-resource-canary-common", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-io-canary", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-sqlite-lint-android-sdk", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-battery-canary", version: MATRIX_VERSION, changing: true
    implementation group: "com.tencent.matrix", name: "matrix-hooks", version: MATRIX_VERSION, changing: true
  }
  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"
    }
  }

1.4 实现 PluginListener,接收 Matrix 处理后的数据, 如:

class MatrixListener(context: Context?) : DefaultPluginListener(context) {
    companion object {
        const val TAG: String = "Matrix.TestPluginListener"
    }
    override fun onReportIssue(issue: Issue) {
        super.onReportIssue(issue)
        MatrixLog.e(TAG, issue.toString())
    }
}

1.5 实现动态配置接口, 可修改 Matrix 内部参数. 在 sample-android 中 我们有个简单的动态接口实例DynamicConfigImplDemo.java, 其中参数对应的 key 位于文件 MatrixEnum中, 摘抄部分示例如下:


class MatrixConfig : IDynamicConfig {
    val isFPSEnable: Boolean
        get() = true
    val isTraceEnable: Boolean
        get() = true
    val isMatrixEnable: Boolean
        get() = true
    override fun get(key: String, defStr: String): String {
        // for Activity leak detect
        if (ExptEnum.clicfg_matrix_resource_detect_interval_millis.name == key || ExptEnum.clicfg_matrix_resource_detect_interval_millis_bg.name == key) {
            Log.d(
                "DynamicConfig",
                "Matrix.ActivityRefWatcher: clicfg_matrix_resource_detect_interval_millis 10s"
            )
            return TimeUnit.SECONDS.toMillis(5).toString()
        }
        if (ExptEnum.clicfg_matrix_resource_max_detect_times.name == key) {
            Log.d(
                "DynamicConfig",
                "Matrix.ActivityRefWatcher: clicfg_matrix_resource_max_detect_times 5"
            )
            return 3.toString()
        }
        return defStr
    }
    override fun get(key: String, defInt: Int): Int {
        //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 == 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 == key) {
            return 10000
        }
        if (MatrixEnum.clicfg_matrix_trace_fps_time_slice.name == key) {
            return 12000
        }
        if (ExptEnum.clicfg_matrix_trace_app_start_up_threshold.name == key) {
            return 3000
        }
        return if (ExptEnum.clicfg_matrix_trace_evil_method_threshold.name == key) {
            200
        } else defInt
    }
    override fun get(key: String, defLong: Long): Long {
        //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 == key) {
            return 10000L
        }
        if (MatrixEnum.clicfg_matrix_resource_detect_interval_millis.name == key) {
            MatrixLog.i(TAG, "$key, before change:$defLong, after change, value:2000")
            return 2000
        }
        return defLong
    }
    override fun get(key: String, defBool: Boolean): Boolean {
        //TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.
        return defBool
    }
    override fun get(key: String, defFloat: Float): Float {
        //TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.
        return defFloat
    }
    companion object {
        private const val TAG = "Matrix.DynamicConfigImplDemo"
    }
}

1.6 选择程序启动的位置对 Matrix 进行初始化,如在 Application 的继承类中, Init 核心逻辑如下


Matrix.Builder builder = new Matrix.Builder(application); // build matrix
  builder.patchListener(new TestPluginListener(this)); // add general pluginListener
  DynamicConfigImplDemo dynamicConfig = new DynamicConfigImplDemo(); // dynamic config
  // init plugin 
  IOCanaryPlugin ioCanaryPlugin = new IOCanaryPlugin(new IOConfig.Builder()
                    .dynamicConfig(dynamicConfig)
                    .build());
  //add to matrix               
  builder.plugin(ioCanaryPlugin);
  //init matrix
  Matrix.init(builder.build());
  // start plugin 
  ioCanaryPlugin.start();

2. 改造Application子类


2.1 模拟Application卡顿


private fun A() {
        B()
        H()
        L()
        SystemClock.sleep(800)
    }
    private fun B() {
        C()
        G()
        SystemClock.sleep(200)
    }
    private fun C() {
        D()
        E()
        F()
        SystemClock.sleep(100)
    }
    private fun D() {
        SystemClock.sleep(20)
    }
    private fun E() {
        SystemClock.sleep(20)
    }
    private fun F() {
        SystemClock.sleep(20)
    }
    private fun G() {
        SystemClock.sleep(20)
    }
    private fun H() {
        SystemClock.sleep(20)
        I()
        J()
        K()
    }
    private fun I() {
        SystemClock.sleep(20)
    }
    private fun J() {
        SystemClock.sleep(6)
    }
    private fun K() {
        SystemClock.sleep(10)
    }
    private fun L() {
        SystemClock.sleep(10000)
    }

2.2 Application.onCreate()调用卡顿方法


override fun onCreate() {
  A()
}

2.3 反射获取ActivityThread的mHandler


override fun attachBaseContext(base: Context?) {
      super.attachBaseContext(base)
      println("zijiexiaozhan MyApp attachBaseContext")
      time1 = SystemClock.uptimeMillis()
      time3 = System.currentTimeMillis()
      try {
          val forName = Class.forName("android.app.ActivityThread")
          val field = forName.getDeclaredField("sCurrentActivityThread")
          field.isAccessible = true
          val activityThreadValue = field[forName]
          val mH = forName.getDeclaredField("mH")
          mH.isAccessible = true
          val handler = mH[activityThreadValue]
          mHandler = handler as Handler
      } catch (e: Exception) {
      }
}

2.4 将原来的onCreate的方法调用转入匿名内部类调用


inner class ApplicationTask : Runnable {
    override fun run() {
        A()
    }
}

2.5 重写Application onCreate方法

override fun onCreate() {
    super.onCreate()
    //重点
    mHandler.postAtFrontOfQueue(ApplicationTask())
}

3.运行,快速定位



3.1 关键字"Trace_EvilMethod"查找日志


tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"MIDDLE","cpu_app":0,"mem":3822452736,"mem_free":1164132,"detail":"NORMAL","cost":1344,"usage":"0.37%","scene":"default","stack":"0,1048574,1,1344\n1,5471,1,1338\n2,17582,1,1338\n3,17558,1,1338\n4,17560,1,379\n5,17562,1,160\n6,17563,1,17\n6,17566,1,20\n6,17568,1,20\n5,17569,1,20\n4,17573,1,56\n5,17575,1,21\n5,17576,1,5\n5,17578,1,10\n4,17580,1,102\n","stackKey":"17558|","tag":"Trace_EvilMethod","process":"com.peter.viewgrouptutorial","time":1624837969986}]

3.2 解析日志 打印卡顿堆栈


android.os.Handler dispatchMessage 1344
.com.peter.viewgrouptutorial.MyApp$ApplicationTask run 1338
..com.peter.viewgrouptutorial.MyApp access$A 1338
...com.peter.viewgrouptutorial.MyApp A 1338
....com.peter.viewgrouptutorial.MyApp B 379
.....com.peter.viewgrouptutorial.MyApp C 160
......com.peter.viewgrouptutorial.MyApp D 17
......com.peter.viewgrouptutorial.MyApp E 20
......com.peter.viewgrouptutorial.MyApp F 20
.....com.peter.viewgrouptutorial.MyApp G 20
....com.peter.viewgrouptutorial.MyApp H 56
.....com.peter.viewgrouptutorial.MyApp I 21
.....com.peter.viewgrouptutorial.MyApp J 5
.....com.peter.viewgrouptutorial.MyApp K 10
....com.peter.viewgrouptutorial.MyApp L 102


相关文章
|
1月前
|
算法 数据处理 Android开发
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
50 2
|
17天前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
22天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
30 5
|
22天前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
43 3
|
1月前
|
存储 缓存 网络协议
5个Android性能优化相关的深度面试题
本文涵盖五个Android面试题及其解答,包括优化应用启动速度、内存泄漏的检测与解决、UI渲染性能优化、减少内存抖动和内存溢出、优化网络请求性能。每个问题都提供了详细的解答和示例代码。
28 2
|
1月前
|
监控 测试技术 Android开发
掌握安卓性能优化的关键策略
【10月更文挑战第7天】 在移动应用开发领域,性能优化是一项至关重要的任务。本文将探讨安卓应用性能优化的重要性、关键策略以及实际操作建议,帮助开发者提升应用的用户体验和竞争力。通过深入浅出的方式,我们将从背景介绍到具体实践,全面解析安卓性能优化的各个维度。
|
2月前
|
Java 测试技术 Android开发
Android性能测试——发现和定位内存泄露和卡顿
本文详细介绍了Android应用性能测试中的内存泄漏与卡顿问题及其解决方案。首先,文章描述了使用MAT工具定位内存泄漏的具体步骤,并通过实例展示了如何分析Histogram图表和Dominator Tree。接着,针对卡顿问题,文章探讨了其产生原因,并提供了多种测试方法,包括GPU呈现模式分析、FPS Meter软件测试、绘制圆点计数法及Android Studio自带的GPU监控功能。最后,文章给出了排查卡顿问题的四个方向,帮助开发者优化应用性能。
181 4
Android性能测试——发现和定位内存泄露和卡顿
|
2月前
|
存储 Java 编译器
🔍深入Android底层,揭秘JVM与ART的奥秘,性能优化新视角!🔬
【9月更文挑战第12天】在Android开发领域,深入了解其底层机制对提升应用性能至关重要。本文详述了从早期Dalvik虚拟机到现今Android Runtime(ART)的演变过程,揭示了ART通过预编译技术实现更快启动速度和更高执行效率的奥秘。文中还介绍了ART的编译器与运行时环境,并提出了减少DEX文件数量、优化代码结构及合理管理内存等多种性能优化策略。通过掌握这些知识,开发者可以从全新的角度提升应用性能。
66 11
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
153 0
|
3月前
|
缓存 Android开发 UED
安卓应用开发中的性能优化实践
【8月更文挑战第31天】在安卓的世界里,性能是王道。本文将带你深入理解如何通过代码优化和工具使用来提升你的安卓应用性能。我们将一起探索内存管理、布局优化、多线程处理等关键领域,并配以实用的代码示例,让你的应用飞一般地运行起来!
下一篇
无影云桌面