一分钟快速定位Android启动耗时问题

目录
  • 前言
  • 1. 接入Tencent Matrix
  • 2. 改造Application子类
  • 3.运行,快速定位
  • 总结

前言

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

总结

到此这篇关于快速定位Android启动耗时问题的文章就介绍到这了,更多相关定位Android启动耗时内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈android性能优化之启动过程(冷启动和热启动)

    本文介绍了浅谈android性能优化之启动过程(冷启动和热启动) ,分享给大家,具体如下: 一.应用的启动方式 通常来说,启动方式分为两种:冷启动和热启动. 1.冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动. 2.热启动:当启动应用时,后台已有该应用的进程(例:按back键.home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热

  • Android实战APP启动速度优化

    APP启动速度非常重要,APP启动速度慢,可能会造成用户体验不良好,尤其是在最近用Android studio之后,如果长时间不打开app,启动速度就会特别的慢,下面我们一起探讨一下影响app启动速度的原因,以及解决方案. 检测启动时间 首先我们要知道app的启动时间,然后你也可以凭着感觉来,这里我教大家一个装逼的方法: adb shell am start -W [packageName]/[.MainActivity] 用adb命令可以检测启动时间,示例如下: ./adb shell am

  • Android优化应用启动速度

    一.应用的启动 启动方式 通常来说,在安卓中应用的启动方式分为两种:冷启动和热启动. 1.冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动. 2.热启动:当启动应用时,后台已有该应用的进程(例:按back键.home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动. 特点 1.冷启动:冷启动因为系统会重新创建一个新的进程分配给它,

  • Android开发之设置开机自动启动的几种方法

    Android开发之设置开机自动启动的几种方法 方法一: <!-- 开机启动 --> <receiver android:name=".AutoStartReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <category android:name="android.inten

  • Android优化之启动页去黑屏实现秒启动

    前言 还记得之前我们写了一篇文章,基于RxJava实现酷炫启动页,然而当我们点击桌面图标启动APP时,有时会闪一下黑色背景,有时黑色背景时间还比较长,哎呀,难看死了,这个怎么办捏,别方,我们今天就来看看启动页的优化. 一.消除启动时的黑屏 点击桌面launcher图标启动APP,闪现的黑色背景其实是出现在我们看到界面第一帧之前.那我们就要想办法让这个黑色的背景变成用户喜欢看到的画面或者让它透明化.有了思路方法也就粗现了,我们有下面两种方案: 自定义WelcomActivity的Theme 说白了

  • Android启动画面的实现方法

    本文实例讲述了Android启动画面的实现方法.分享给大家供大家参考.具体分析如下: 在应用程序中经常用到启动画面,会启动一个后台线程为主程序的运行准备资源. Android要实现启动画面可以这样做: 这是splash.xml布局文件的代码: 复制代码 代码如下: <LinearLayout   xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_height="fill

  • Android APP启动方式、启动流程及启动优化分析

    本文章向大家介绍Android app应用启动的一些相关知识,包括app启动方式.app启动流程和app启动优化等知识!  app应用启动方式 1.冷启动 当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动.冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量.布局.绘制),最后显示在界面上. 2.热启动 当启动应用时,后台已有该应用的进程(例:按

  • Android 优化之app启动优化的实现

    App 启动方式 冷启动 App 没有启动过或 App 进程被杀,系统中不存在该 App 进程,此时启动即为冷启动.需要创建 App 进程,加载相关资源,启动 Main Thread,初始化首屏 Activity 等.在这个过程中,屏幕会显示一个空白的窗口(颜色基于主题),直至首屏 Activity 完全启动. 热启动 热启动意味着 App 进程只是处于后台,系统只是将其从后台带到前台展示给用户.类同与冷启动,在这个过程中,屏幕会显示一个空白的窗口(颜色基于主题),直至 activity 渲染完

  • 一分钟快速定位Android启动耗时问题

    目录 前言 1. 接入Tencent Matrix 2. 改造Application子类 3.运行,快速定位 总结 前言 Tencent Matrix默认无法监测Application冷启动的耗时方法,本文介绍了如何改造Matrix支持冷启动耗时方法监测.让你一分钟就能给App启动卡顿号脉. 1. 接入Tencent Matrix 1.1 在你项目根目录下的 gradle.properties 中配置要依赖的 Matrix 版本号,如: MATRIX_VERSION=1.0.0 1.2 在你项目

  • 5分钟快速实现Android爆炸破碎酷炫动画特效的示例

    这个破碎动画,是一种类似小米系统删除应用时的爆炸破碎效果的动画. 效果图展示 先来看下是怎样的动效,要是感觉不是理想的学习目标,就跳过,避免浪费大家的时间.�� 源码在这里:point_right: https://github.com/ReadyShowShow/explosion 一行代码即可调用该动画 new ExplosionField(this).explode(view, null)) 下面开始我们酷炫的Android动画特效正式讲解:point_down: 先来个整体结构的把握 整

  • Android仿微信通讯录滑动快速定位功能

    先给大家展示下效果图: 实现代码如下: 下面简单说下实现原理. public class IndexBar extends LinearLayout implements View.OnTouchListener { private static final String[] INDEXES = new String[]{"#", "A", "B", "C", "D", "E", &qu

  • Android开发实现根据字母快速定位侧边栏

    按首字母对List排列,并根据首字母快速定位的实现,在Android开发中被大量应用,今天我也来亲自实现一下,将这个控件封装起来,也方便以后的使用.大体上可以分为两步来实现这个控件:首先使自己的控件继承于View,并进行图形绘制:然后根据触摸位置计算当前触摸的字母,并实现回调接口的方法. 下面来进行实践: 1.创建自己的控件类并继承于View,注意:不能只声明含有一个构造参数Context的构造函数,这样我们的控件无法在xml文件中调用,因为Android中xml调用控件之间的参数传递是通过构造

  • 快速解决Tomcat启动慢的问题,超简单

    今天在帮一位同学解决了一个问题--Tomcat启动超级慢,大概五六分钟.解决之后,只需要3秒钟即可启动. 问题怎么解决呢? 在Tomcat的bin目录下找到catalina.sh,然后打开它,在以下位置添加一行代码: -Djava.security.egd=file:/dev/urandom 2019年02月12日补充:很多朋友想知道原理,我就简单说明一下. Tomcat 7和Tomcat 8在启动的时候会调用org.apache.catalina.util.SessionIdGenerator

  • MySQL如何快速定位慢SQL的实战

    目录 开启慢查询日志 系统变量 修改配置文件 设置全局变量 分析慢查询日志 mysqldumpslow pt-query-digest 开启慢查询日志 在项目中我们会经常遇到慢查询,当我们遇到慢查询的时候一般都要开启慢查询日志,并且分析慢查询日志,找到慢sql,然后用explain来分析 系统变量 MySQL和慢查询相关的系统变量如下 参数 含义 slow_query_log 是否启用慢查询日志, ON为启用,OFF为没有启用,默认为OFF log_output 日志输出位置,默认为FILE,即

  • MySQL实战记录之如何快速定位慢SQL

    目录 开启慢查询日志 系统变量 修改配置文件 设置全局变量 分析慢查询日志 mysqldumpslow pt-query-digest 用法实战 总结 开启慢查询日志 在项目中我们会经常遇到慢查询,当我们遇到慢查询的时候一般都要开启慢查询日志,并且分析慢查询日志,找到慢sql,然后用explain来分析 系统变量 MySQL和慢查询相关的系统变量如下 参数 含义 slow_query_log 是否启用慢查询日志, ON为启用,OFF为没有启用,默认为OFF log_output 日志输出位置,默

  • .NET程序调试技巧(一):快速定位异常的一些方法

    作为一个程序员,解BUG是我们工作中常做的工作,甚至可以说解决问题能力是一个人工作能力的重要体现.因为这体现了一个程序员的技术水平.技术深度.经验等等. 那么在我们解决BUG的过程中,定位问题是非常重要的.有句话叫"发现问题是解决问题的一半. 本文讲述就快速定位异常(专指.NET程序异常)的方法.包括在本机定位异常,在客户环境定位.net程序异常,在客户环境定位SilverLight异常. 一:定位本机异常 在我们本机定位异常很容易.假设我们都是使用的的VisualStudio,那么只需要在调试

  • 反向Ajax 30分钟快速掌握

    场景1:当有新邮件的时候,网页自动弹出提示信息而无需用户手动的刷新收件箱. 场景2:当用户的手机扫描完成页面中的二维码以后,页面会自动跳转. 场景3:在类似聊天室的环境中有任何人发言,所有登录用户都可以即时看见信息. 与传统的MVC模型请求必须从客户端发起由服务器响应相比,使用反向Ajax能够模拟服务器端主动向客户端推送事件从而提高用户体验.本文将分两个部分讨论反向Ajax技术,包括:Comet和WebSocket.文章旨在演示如何实现以上两种技术手段,Struts2或SpringMVC中的应用

  • Android启动页解决方案(推荐)

    启动页几乎成为了每个app的标配,有些商家在启动页中增加了开屏广告以此带来更多的收入.目前启动页的广告都有倒计时的功能,那么我们在倒计时的过程中能做些什么呢? 这篇文章主要包括以下两方面内容 集成腾讯广告联盟的SDK 启动页加载过程中,后台初始化数据 我们在设计启动页时的常规做法是建立一个Activity来加载开屏图片或者广告,作为程序的入口,那么在这个三到五秒时间内如果进行数据下载,当用户点击了跳过按钮或者计时结束了数据还没初始化完成,已经进入了主页面,而主界面刚好需要那些基础数据该如何? 因

随机推荐