Demo:Advanced
线下调试
Linux命令
说到卡顿,主要都和 CPU 相关。刷了几篇CPU文章,造成卡顿的原因最终都会反映到 CPU 时间上。
CPU 时间分为两种:用户时间和系统时间。用户时间就是执行用户态应用程序代码所消耗的时间;系统时间就是执行内核态系统调用所消耗的时间,包括 I/O、锁、中断以及其他系统调用的时间。
Linux环境下进程的CPU占用率,获取手机cpu方法
E:\zt\project\jitpack\Advanced>adb shell
// 获取 CPU 核心数
heroqltechn:/ $ cat /sys/devices/system/cpu/possible
0-3
// 获取某个 CPU 的频率
heroqltechn:/ $ cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
1593600
Traceview
Traceview 可以用来查看整个过程有哪些函数调用,但是工具本身带来的性能开销过大,有时无法反映真实的情况。比如一个函数本身的耗时是 1 秒,开启 Traceview 后可能会变成 5 秒,而且这些函数的耗时变化并不是成比例放大。
Nanoscope
另外介绍一款,Uber 开源的Nanoscope就能达到这个效果。它的实现原理是直接修改 Android 虚拟机源码,在ArtMethod执行入口和执行结束位置增加埋点代码,将所有的信息先写到内存,等到 trace 结束后才统一生成结果文件。
但是它也有不少限制:需要自己刷 ROM,并且当前只支持 Nexus 6P,或者采用其提供的 x86 架构的模拟器。默认只支持主线程采集,其他线程需要代码手动设置。考虑到内存大小的限制,每个线程的内存数组只能支持大约 20 秒左右的时间段。
systrace
利用了 Linux 的ftrace调试工具,相当于在系统各个关键位置都添加了一些性能探针,也就是在代码里加了一些性能监控的埋点。
systrace 跟踪系统的 I/O 操作、CPU 负载、Surface 渲染、GC 等事件。
systrace 工具有选择性或者采用抽样的方式观察某些函数调用过程,只能监控特定系统调用的耗时情况,而且性能开销非常低。但是它不支持应用程序代码的耗时分析,所以在使用时有一些局限性。
我们可以通过编译时给每个函数插桩的方式来实现,也就是在重要函数的入口和出口分别增加Trace.beginSection和Trace.endSection。
注意
AS在3.0之后删除之前的了 Tools
目录下的Android
菜单,这样就导致以前通过Tools——>Android——>DeviceMonitor打开DDMS的方式行不通啦
;
最后根据Google 文档自己鼓捣打开DDMS,在此记录一下:
1.先找到配置的SDK路径,我的是E:\android-sdk\sdk;
2.在SDK的android路径tools下monitor.bat 的批处理文件;
3.点击monitor.bat这个批处理文件,在屏幕上会打开一个类似CMD的命令行中输入板,然后迅速自动关闭
;
4.坐等1到3秒就会打开DDMS;
Simpleperf
如果需要分析 Native 代码的耗时,可以选择 Simpleperf;如果想分析系统调用,可以选择 systrace;如果想分析整个程序执行流程的耗时,可以选择 Traceview 或者插桩版本的 systrace。
Profiler
Android Studio 3.2 的 Profiler 中直接集成了几种性能分析工具,其中:Sample Java Methods 的功能类似于 Traceview 的 sample 类型。Trace Java Methods 的功能类似于 Traceview 的 instrument 类型。Trace System Calls 的功能类似于 systrace。SampleNative (API Level 26+) 的功能类似于 Simpleperf。
线上监控
消息队列
通过替换 Looper 的 Printer 实现。
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
也是BlockCanary原理,中文文档
通过一个监控线程,每隔一段时间向主线程消息队列的头部插入一条空消息,然后监测空消息是否被消费。
插桩
使用Hook 技术。
参考demo中的CartonActivity页面
@TimeSpend("btnTime")
@Override
public void onClick(View v) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
输出日志
com.example.advanced E/TimeSpendAspect: 功能:btnTime,类的onClick方法执行了,用时1000 ms
Profilo
Facebook 开源了一个叫Profilo的库,集成 atrace 功能,快速获取 Java 堆栈。
Profilo 的实现非常精妙,它实现类似 Native 崩溃捕捉的方式快速获取 Java 堆栈,通过间隔发送 SIGPROF 信号,整个过程如下图所示。