前言

无论是启动,内存,布局等等这些优化,最终的目的就是为了应用不卡顿。应用的体验性好坏,最直观的表现就是应用的流畅程度,用户不知道什么启动优化,内存不足,等等,应用卡顿,那么这个应用就不行,被卸载的概率非常大。所以说为了保证用户留存率,卡顿优化是非常非常的重要。在这篇文章,咱们不讨论是什么原因造成卡顿,其实在前面写的性能优化文章中,都是造成卡顿的原因,需要需要做好卡顿优化,最好从头开始一步一步来处理。今天我们主要是介绍一些针对卡顿检测的一些工具使用。

检测卡顿常用工具

Systrace

Systrace这个工具在《布局优化》一章节中已经介绍过了,这里就不在赘述。地址:https://www.cnblogs.com/huangjialin/p/13353541.html

StrictMode的使用

StrictMode,严苛模式。StrictMode是在Android开发过程中不可缺少的性能检测工具,它能够检测出在开发过程中不合理的代码块,非常方便。

策略分类

StrictMode分为线程策略(ThreadPolicy)和虚拟机策略(VmPolicy)

使用方式
  1. //开启Thread策略模式
  2. StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
  3. .detectNetwork()//监测主线程使用网络io
  4. // .detectCustomSlowCalls()//监测自定义运行缓慢函数
  5. // .detectDiskReads() // 检测在UI线程读磁盘操作
  6. // .detectDiskWrites() // 检测在UI线程写磁盘操作
  7. .detectAll()
  8. .penaltyLog() //写入日志
  9. .penaltyDialog()//监测到上述状况时弹出对话框
  10. .build());
  11. //开启VM策略模式
  12. StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
  13. // .detectLeakedSqlLiteObjects()//监测sqlite泄露
  14. // .detectLeakedClosableObjects()//监测没有关闭IO对象
  15. // .setClassInstanceLimit(MainActivity.class, 1) // 设置某个类的同时处于内存中的实例上限,可以协助检查内存泄露
  16. // .detectActivityLeaks()
  17. .detectAll()
  18. .penaltyLog()//写入日志
  19. .build());

上面基本都注释好了,这里就不在一一说明了。如果我们在开发过程中,能够通过StrictMode这个工具类来规避掉这些问题,那么将会大大的减少很多性能相关的问题。

BlockCanary使用

我们先看看怎么使用,然后在看BlockCanary

依赖

  1. debugImplementation 'com.github.bzcoder:blockcanarycompat-android:0.0.4'

在application中

  1. BlockCanary.install(mContext, appBlockCanaryContext).start();

appBlockCanaryContext类

  1. /**
  2. * BlockCanary配置的各种信息
  3. */
  4. public class AppBlockCanaryContext extends BlockCanaryContext {
  5. /**
  6. * Implement in your project.
  7. *
  8. * @return Qualifier which can specify this installation, like version + flavor.
  9. */
  10. public String provideQualifier() {
  11. return "unknown";
  12. }
  13. /**
  14. * Implement in your project.
  15. *
  16. * @return user id
  17. */
  18. public String provideUid() {
  19. return "uid";
  20. }
  21. /**
  22. * Network type
  23. *
  24. * @return {@link String} like 2G, 3G, 4G, wifi, etc.
  25. */
  26. public String provideNetworkType() {
  27. return "unknown";
  28. }
  29. /**
  30. * Config monitor duration, after this time BlockCanary will stop, use
  31. * with {@code BlockCanary}'s isMonitorDurationEnd
  32. *
  33. * @return monitor last duration (in hour)
  34. */
  35. public int provideMonitorDuration() {
  36. return -1;
  37. }
  38. /**
  39. * Config block threshold (in millis), dispatch over this duration is regarded as a BLOCK. You may set it
  40. * from performance of device.
  41. *
  42. * @return threshold in mills
  43. */
  44. public int provideBlockThreshold() {
  45. return 500;
  46. }
  47. /**
  48. * Thread stack dump interval, use when block happens, BlockCanary will dump on main thread
  49. * stack according to current sample cycle.
  50. * <p>
  51. * Because the implementation mechanism of Looper, real dump interval would be longer than
  52. * the period specified here (especially when cpu is busier).
  53. * </p>
  54. *
  55. * @return dump interval (in millis)
  56. */
  57. public int provideDumpInterval() {
  58. return provideBlockThreshold();
  59. }
  60. /**
  61. * Path to save log, like "/blockcanary/", will save to sdcard if can.
  62. *
  63. * @return path of log files
  64. */
  65. public String providePath() {
  66. return "/blockcanary/";
  67. }
  68. /**
  69. * If need notification to notice block.
  70. *
  71. * @return true if need, else if not need.
  72. */
  73. public boolean displayNotification() {
  74. return true;
  75. }
  76. /**
  77. * Implement in your project, bundle files into a zip file.
  78. *
  79. * @param src files before compress
  80. * @param dest files compressed
  81. * @return true if compression is successful
  82. */
  83. public boolean zip(File[] src, File dest) {
  84. return false;
  85. }
  86. /**
  87. * Implement in your project, bundled log files.
  88. *
  89. * @param zippedFile zipped file
  90. */
  91. public void upload(File zippedFile) {
  92. throw new UnsupportedOperationException();
  93. }
  94. /**
  95. * Packages that developer concern, by default it uses process name,
  96. * put high priority one in pre-order.
  97. *
  98. * @return null if simply concern only package with process name.
  99. */
  100. public List<String> concernPackages() {
  101. return null;
  102. }
  103. /**
  104. * Filter stack without any in concern package, used with @{code concernPackages}.
  105. *
  106. * @return true if filter, false it not.
  107. */
  108. public boolean filterNonConcernStack() {
  109. return false;
  110. }
  111. /**
  112. * Provide white list, entry in white list will not be shown in ui list.
  113. *
  114. * @return return null if you don't need white-list filter.
  115. */
  116. public List<String> provideWhiteList() {
  117. LinkedList<String> whiteList = new LinkedList<>();
  118. whiteList.add("org.chromium");
  119. return whiteList;
  120. }
  121. /**
  122. * Whether to delete files whose stack is in white list, used with white-list.
  123. *
  124. * @return true if delete, false it not.
  125. */
  126. public boolean deleteFilesInWhiteList() {
  127. return true;
  128. }
  129. /**
  130. * Block interceptor, developer may provide their own actions.
  131. */
  132. public void onBlock(Context context, BlockInfo blockInfo) {
  133. Log.i("lz","blockInfo "+blockInfo.toString());
  134. }
  135. }

就是这么简单,一旦发生卡顿,那么将会以通知的形式在通知栏中显示出对应的堆栈信息(和leakcanary类似,但是两者没有任何关系)。或者在

SD卡中的blockcanary文件夹中生成对应的log日志

那么,问题来了BlockCanary是怎么检测卡顿的呢?大家注意AppBlockCanaryContext类中有一个provideBlockThreshold()方法,return了一个500ms。我们知道Android中消息的分发处理都是通过handler来的,handler中有一个looper对象,looper对象中有一个loop方法,看一下源码

  1. ...
  2. for (;;) {
  3. Message msg = queue.next(); // might block
  4. ...//省略若干
  5. if (logging != null) {
  6. logging.println(">>>>> Dispatching to " + msg.target + " " +
  7. msg.callback + ": " + msg.what);
  8. }
  9. ...//省略若干
  10. try {
  11. msg.target.dispatchMessage(msg);
  12. dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
  13. } finally {
  14. if (traceTag != 0) {
  15. Trace.traceEnd(traceTag);
  16. }
  17. }
  18. ...//省略若干
  19. if (logging != null) {
  20. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  21. }
  22. ...//省略若干
  23. }
  24. ...

应用中的所有事件都是通过dispatchMessage这个方法来进行处理,那么是不是说dispatchMessage执行的时间就是某个事件执行的时间。如果这个时间大于我们在provideBlockThreshold()定义的时间,我们就认为发送了卡顿了,需要优化,就会获取到对应的堆栈信息保存起来并提示。

ANR-WatchDog介绍

发生卡顿,严重时很容易导致anr,我们知道发生anr的情况基本上就是以下几种情况

1、触摸事件在5s没有得到响应。

2、Bradcast 是10s,后台广播是60s

3、service:前台service 20s 后台service 200s

用户在使用应用时,出现ANR,那么体验将会是非常非常差,现在很多手机厂商为了用户体验,去掉了这个ANR弹框,但是还是会一直卡在这里。

ANR-WatchDog 使用

添加依赖

  1. implementation 'com.github.anrwatchdog:anrwatchdog:1.3.0'

在application中的onCreate()方法

  1. new ANRWatchDog().setIgnoreDebugger(true).start();

setIgnoreDebugger 是指忽略点断点的情况

当然如果这样设置的话,一旦出现ANR,那么ANR-Watchdog是会将应用杀死的,如果不想杀死应用那么需要设置一个监听

  1. new ANRWatchDog().setANRListener(new ANRWatchDog.ANRListener() {
  2. @Override
  3. public void onAppNotResponding(ANRError error) {
  4. // Handle the error. For example, log it to HockeyApp:
  5. }
  6. }).start();

ANR-WatchDog 原理

我用自己的话来总结一下原理:前面我们说了所有的消息都是通过handler中的dispatchMessage方法进行分发的,而ANR-WatchDog 原理就是通过dispatchMessage的时间长度来判断是否出现anr的,过程大概是这样的:ANR-WatchDog 开启一个监控线程,并向主线程发一个消息,然后自己休眠5s,5s后看主线程有没有处理这个消息,如果处理了,那么继续发送,进入一下次检查。如果没有处理,说明主线程发生了阻塞,收集对应的anr信息。

Lancet的介绍

前面写的几篇性能优化文章基本都介绍有对应的hook框架,这里再介绍一个AOP框架---Lancet,这几个框架的比对,后面会再开一篇。这里先看看怎么使用。Lancet是一个轻量级Android AOP框架,特点:

1、编译速度快,并且支持增量编译

2、简洁的API,几行代码完成注入需求

3、没有任何多余代码插入apk

4、支持用于SDK,可以在SDK编写注入代码来修改依赖SDK的APP

使用方式

在根目录的build.gradle

dependencies{

classpath 'me.ele:lancet-plugin:1.0.5'

}

在APP目录的build.gradle

apply plugin: 'me.ele.lancet'

dependencies {

provided 'me.ele:lancet-base:1.0.5'

}

开一个类aaaa.java

@Proxy("i")

@TargetClass("android.util.Log")

public static int anyName(String tag, String msg){

msg = msg + "lancet";

return (int) Origin.call();

}

这样,就完成了,当我们通过Log.i("lancet","你好“);来打印日志的时候,输出的文本先被Lancet出来后,在输出。"你好,lancet"

常见卡顿问题解决方案

1、内存抖动的问题,GC过于频繁

2、方法太耗时了(CPU占用)

3、布局过于复杂,渲染速度慢

....

Android性能优化----卡顿优化的更多相关文章

  1. android中app卡顿优化问题

     所谓app卡顿原因就是在运行时出现了丢帧,还可能是UI线程被阻塞.首先来一下丢帧现象,android每16ms会对界面进行一次渲染,如果app的绘制.计算等超过了16ms那么只能等下一个16ms才能 ...

  2. Android TextView setText卡顿问题

    TextView 是经常使用控件之中的一个,最经常使用的方法是setText()  . 可是 我们在显示大量的文本的时候,使用setText还是会有一些性能的问题. 这篇文章 关于TextView的s ...

  3. android TextView SetText卡顿原因

    [android TextView SetText卡顿原因] 不要用wrap_content即可. 参考:http://blog.csdn.net/self_study/article/details ...

  4. Android 界面滑动卡顿分析与解决方案(入门)

    Android 界面滑动卡顿分析与解决方案(入门) 导致Android界面滑动卡顿主要有两个原因: 1.UI线程(main)有耗时操作 2.视图渲染时间过长,导致卡顿 目前只讲第1点,第二点相对比较复 ...

  5. Android 卡顿优化 2 渲染优化

    1.概述 2015年初google发布了Android性能优化典范,发了16个小视频供大家欣赏,当时我也将其下载,通过微信公众号给大家推送了百度云的下载地址(地址在文末,ps:欢迎大家订阅公众号),那 ...

  6. Android 卡顿优化 1 卡顿解析

    1, 感知卡顿 用户对卡顿的感知, 主要来源于界面的刷新. 而界面的性能主要是依赖于设备的UI渲染性能. 如果我们的UI设计过于复杂, 或是实现不够好, 设备又不给力, 界面就会像卡住了一样, 给用户 ...

  7. Android 卡顿优化 3 布局优化 工具 Hierarchy Viewer

    欲善其事, 先利其器. 分析布局, 就不得不用到Hierarchy Viewer了. 本文工具使用皆以GithubApp的详情界面RepoDetailActivity为例说明. 为了不影响阅读体验, ...

  8. Android 布局渲染流程与卡顿优化

    文章内容概要 一.手机界面UI渲染显示流程 二.16ms原则 三.造成卡顿的原因 四.过度绘制介绍.检测工具.如何避免造成过度绘制造成的卡顿 一.手机界面UI渲染显示流程 大家都知道CPU(中央处理器 ...

  9. GC 卡顿 优化 三色标记优势

    小结: 1. 三色标记的一个明显好处是能够让用户程序和 mark 并发的进行 Go GC 卡顿由秒级降到毫秒级以下:到底做了哪些优化? https://mp.weixin.qq.com/s/2BMGG ...

随机推荐

  1. java List的初始化

    今天在处理生成excel的时候用到了java的list,但是需要直接赋值固定的几个变量,如果先初始化然后add的方法: List<String> name = new ArrayList( ...

  2. Java synthetic

    读完这篇文章你将会收获到 synthetic fields synthetic method synthetic class 概述 上一篇 Java 枚举 提及到编译成 class 文件之后.编译器会 ...

  3. 接口&&多态&&构造函数&&关键字

    day06 抽象类的局限性(与接口的区别) 抽象类可以定义非抽象方法,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法;jdk1.8之后接口可以包含默认方法. 一个类只能继承一个直接父类 ...

  4. EOS基础全家桶(十四)智能合约进阶

    简介 通过上一期的学习,大家应该能写一些简单的功能了,但是在实际生产中的功能需求往往要复杂很多,今天我就继续和大家分享下智能合约中的一些高级用法和功能. 使用docker编译 如果你需要使用不同版本的 ...

  5. mssql 手工注入流程小结

    对于MSSQL的注入点,无外乎这三种权限:SA,DB_OENER,PUBLIC.SA(System Admin)权限我们可以直接执行命令,DB_OENER权限的话,我们可以找到WEB的路径,然后用备份 ...

  6. TensorFlow中的显存管理器——BFC Allocator

    背景 作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor] 使用GPU训练时,一次训练任务无论是模型参数还是中间结果都需要占用大量显存.为了 ...

  7. HDU 4352 XHXJ's LIS HDU(数位DP)

    HDU 4352 XHXJ's LIS HDU 题目大意 给你L到R区间,和一个数字K,然后让你求L到R区间之内满足最长上升子序列长度为K的数字有多少个 solution 简洁明了的题意总是让人无从下 ...

  8. adb devices 不能连接设备 could not install *smartsocket* listener

    cmd以管理员身份运行命令adb devices  或adb reverse tcp:8081 tcp:8081,无法连接设备,出现上图信息. 输入命令:adb kill-server 再输入:adb ...

  9. flutter学习01-flutter起步安装配置(window, vscode开发)

    从零开始配置flutter环境,如果直接去看官方文档配置的话,太过复杂,其实正式没有那么多步骤,记录一下: 1.首先,前往下面这个网站,下载flutter sdk  https://flutter.d ...

  10. CRM开发系列

    CRM,客户关系管理系统(Customer Relationship Management).企业用CRM技术来管理与客户之间的关系,以求提升企业成功的管理方式,其目的是协助企业管理销售循环:新客户的 ...