Android 开发高手课
张绍文
前微信高级工程师,Tinker 负责人
52721 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 62 讲
导读 (1讲)
模块一 高质量开发 (25讲)
Android 开发高手课
15
15
1.0x
00:00/00:00
登录|注册

06补充篇 | 卡顿优化:卡顿现场与卡顿分析

线程间的死锁和热锁分析
Suspended状态
"BackgroundHandler"线程
WAITING状态
BLOCKED状态
PV卡顿率
UV卡顿率
I/O和网络相关
内存相关信息
CPU使用率和调度信息
性能问题
可行性问题
疑问二:获得ANR日志
疑问一:Native线程状态
步骤二:获得所有线程堆栈
步骤一:获得Java线程状态
卡顿树
卡顿率
现场信息
Hook实现
SIGQUIT信号实现
Java实现
卡顿分析
卡顿现场
课后作业
总结
卡顿现场与卡顿分析
卡顿优化

该思维导图由 AI 生成,仅供参考

我们使用上一期所讲的插桩或者 Profilo 的方案,可以得到卡顿过程所有运行函数的耗时。在大部分情况下,这几种方案的确非常好用,可以让我们更加明确真正的卡顿点在哪里。
但是,你肯定还遇到过很多莫名其妙的卡顿,比如读取 1KB 的文件、读取很小的 asset 资源或者只是简单的创建一个目录。
为什么看起来这么简单的操作也会耗费那么长的时间呢?那我们如何通过收集更加丰富的卡顿现场信息,进一步定位并排查问题呢?

卡顿现场

我先来举一个线上曾经发现的卡顿例子,下面是它的具体耗时信息。
从图上看,Activity 的 onCreate 函数耗时达到 3 秒,而其中 Lottie 动画中openNonAsset函数耗时竟然将近 2 秒。尽管是读取一个 30KB 的资源文件,但是它的耗时真的会有那么长吗?
今天我们就一起来分析这个问题吧。
1. Java 实现
进一步分析 openNonAsset 相关源码的时候发现,AssetManager 内部有大量的 synchronized 锁。首先我怀疑还是锁的问题,接下来需要把卡顿时各个线程的状态以及堆栈收集起来做进一步分析。
步骤一:获得 Java 线程状态
通过 Thread 的 getState 方法可以获取线程状态,当时主线程果然是 BLOCKED 状态。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了卡顿优化的重要性,并提出了通过收集更丰富的卡顿现场信息来定位和排查问题的方法。作者指出,尽管插桩或Profilo等方案可以帮助明确真正的卡顿点在哪里,但有时仍会遇到一些莫名其妙的卡顿,比如读取小文件或创建目录等操作也会耗费较长时间。因此,文章提出了通过收集更丰富的卡顿现场信息来进一步定位和排查问题的方法。这种方法可以帮助开发者更全面地了解卡顿问题的根源,从而更有效地进行优化。 文章通过具体案例分析了卡顿问题的定位与解决方法,包括Java实现、SIGQUIT信号实现和Hook实现等。作者深入剖析了线程状态、堆栈信息和ANR日志,以及通过Hook方式实现了“无损”获取所有Java线程堆栈与详细信息的方法。此外,文章还提出了进一步增加CPU使用率、内存相关信息、I/O和网络相关信息等,使卡顿的“现场信息”比系统ANR日志更加丰富的建议。 总之,本文通过深入的技术分析和案例研究,为读者提供了丰富的卡顿优化方法和技术思路,对于需要解决卡顿问题的技术人员具有重要的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Android 开发高手课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(30)

  • 最新
  • 精选
  • 郭威
    问一下作者那个fork获取线程堆栈的方式说的太笼统了,有小demo么

    作者回复: 先留时间给大家实现,后续看情况要不要放出来

    2018-12-19
    12
  • Sean
    文中说明的 " 需要注意的是在 Android 7.0 之后,getAllStackTraces 是不会返回主线程的堆栈的",我在8.0和8.1的系统下Java 层通过代码测试过,发现主线程的stacktrace实际上是可以得到的。不知所述结论是如何得到的?

    作者回复: 这里笔误了,应该是Android 7.0,没有之后。我后面改一下

    2018-12-23
    8
  • 1874
    老师好,通过breakpad方案获取native堆栈时能关联上java层的堆栈吗?thread id是对应不上的。根据threadname?c层子线程要是没命名应该会有很多重复吧,期待老师解答

    作者回复: native堆栈对应不上java线程的堆栈的,线程id 也是对应不上的,java的堆栈是独立在虚拟机里的和native执行的堆栈对应不上,那个只能对应到虚拟机的执行栈

    2019-05-16
    3
  • X
    老师好,工作中的确遇到过类似开篇描述的卡顿现场,只不过当时是操作一个三方数据库,主线程里初始化了数据库有关的实体对象,然后又进行了数据库异步查找,结果ANR. 查看源码发现,两个操作都用的该数据库核心类的类锁,导致主线程一直等待子线程是释放锁。这个不是线上的,是线下的,所以查找很快,解决方式是把把异步查找用handler post了一个runnable去操作,确保主线程初始化该数据库的操作在这个异步查找之前,即先得到锁。 所以这里顺便问下: 1.上述线程抢占导致的ANR处理方法是否妥当? 2. 文中说到SIGQUIT性能差,那个不是模仿系统ANR机制么 ,而那个黑科技黑科技也是模仿ANR日志打印,为何就比前者好呢? 3.另外想问下那个文中提到的抽样是客户端控制还是服务端控制的啊?

    作者回复: 1. 数据库要case by case 2. Sigquit是卡在当前进程操作,黑科技是卡在子进程操作 3. 服务端

    2018-12-18
    2
  • Geek_2d38e3
    张老师,请教一下,黑科技手机线程堆栈那个方案,我成功调用了DumpState函数,但是函数执行完毕后,传入的std::ostream里面没有内容,您有遇到过这种情况吗?

    作者回复: 需要慢慢调试一下哈,是可以实现的

    2019-07-18
    2
    1
  • Fred
    老师好,在应用开发时发现同一个方法,拥有相同的输入参数,在不同的Activity里面执行的耗时会不一样。对于这个问题应该从那些角度去分析呢?

    作者回复: 通过第七章systrace的方法可以分析具体的差异情况

    2018-12-25
    1
  • 1874
    老师好,在native层hook子进程获取java堆栈的Demo能放出来吗?期待

    作者回复: 主要是通过Hook跟日志,定位是哪些view的问题

    2019-03-27
  • catkin
    张老师,文中说的fork进程收集,但是在高版本中好像fork出来的进程不能执行啊!

    作者回复: 什么意思?fork机制在很多场景都有使用,android系统也是这样。

    2019-02-22
  • sjx
    项目中使用的lottie,CPU占用非常高,大家有什么解决方案吗?

    作者回复: 需要进一步拆解下去具体的原因,是哪些步骤耗时

    2019-01-05
  • Juinn
    shaowen老师,课后作业中,hook线程的创建,为什么只有在sample进程有效,so库不是共享的吗

    作者回复: plt hook只能hook本进程的,对其他进程是无效的

    2019-01-01
收起评论
显示
设置
留言
30
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部