Android 使用systrace进行卡顿分析

前面说到使用traceview来分析启动速度,但是traceview用于分析卡顿并不是很合适的,原因是traceview对性能的损耗很大,即使不卡顿使用traceview之后也会卡得飞起

1.调用方法

systrace可以通过命令直接调用,也可以通过sdk里的android device monitor 调用

systrace所在的目录:AndroidSdk\platform-tools\systrace\systrace.py

android device monitor :AndroidSdk\tools\monitor.bat (实际上调用的也是 systrace.py )

打开 monitor 后选中 systrace 可以看到以下内容

monitor界面

系统根据不同的category,如Graphics绘制 Cpu调度 等 已经在源码里的关键位置提前埋好了点。选项中的Appliation表示用户app自己埋的点是否启用

例如绘制的关键函数performTraversals 渲染线程的关键函数 dequebuufer等都可以在开启相应TAG的时候看见。

对于使用命令 systrace.py -t 10 sched gfx view wm am app webview -a <package-name> 与gui的选项对于, gfx表示图形view表示view system

2:线程状态查看:

在线程的最顶部,展示了当前线程的运行状态,注意非常重要!按住shift然后单击,可以查看线程当前状态对应如下四个

Running表示运行中,sleeping表示锁等待或者sleep中

对于发现线程由sleep进入runable的情况,按住shift单击,可以从右下角看到来自什么线程的唤醒

runnable状态详情

3.自定义埋点

Trace.beginSection(“onCreate”);

Trace.endSection();

Trace之间可以交叉,但必须成对出现

4 .UI线程调度问题导致的卡顿

需要注意的是,如果在非绘制过程往主线程中post任务然后执行耗时的操作,从systrace来看每一帧并不会显示为黄色或者红色,这个时间可能已经非常卡了

例如这种代码

此时systrace显示结果是:

帧间耗时无法展示

此时可以使用traceview或者前一节所述查看线程状态来分析原因

5.UI线程绘制问题导致的卡顿

绘制过程中,即measue layout draw 过程中 UI线程卡顿表现形式一般如下图,ui线程飘红但是RenderThread还是绿色

帧处理过程中主线程导致的卡顿

systrace给出了4个原因分别对应以下四个情况

1)在view的绘制中有锁竞争或者Sleep

systrace整体图与前面基本一致不再贴出,按住shift点击某一帧显示

可以看到锁竞争也是归入sleeping的

注意一个技巧

2)在view的绘制时中有复杂计算

systrace图与前面基本一致, 按住shift 点击其中一帧可以看到

有1秒的时间消耗在Running状态中

3)绘制过程中假有IO操作

可以看到io操作包含sleep以及blockingI/O dealy

4) 线程过多导致cpu繁忙

可以看到线程繁忙的情况显示结果为 not scheduled,but runnable,意思是可以运行但没有cpu时间片

分析步骤

1)按住shift单机掉帧的帧标记

2)首先查看掉帧来自哪个阶段,measure阶段,layout阶段 , 或者draw阶段

3)如果丢帧原因主要显示为Running,需要考虑是是否在对应过程中有复杂的计算

4)如果丢帧原因主要显示为Sleep ,需要考虑是是否在对应过程中有锁竞争或者 sleep

5)如果丢帧原因主要显示为IO,需要考虑在对应过程是否有IO操作

6)如果丢帧原因主要显示not scheduled,but runnable ,需要考虑是否线程资源被占用,可以考虑降低其他线程优先级或者延迟启动

另外可以使用 am trace-ipc start 来跟踪是否有频繁的binder调用

5.Render线程导致的卡顿

一般来说,越复杂的ui 会导致ui线程和render线程都繁忙,但是有一些操作,在主线程并不耗时,然而在渲染线程中耗时,原因在于主线程在硬件绘制过程中仅仅是保存绘制指令。在渲染线程中执行指令的时候可能会非常耗时,

Bitmap upload纹理导致:

例如在绘制大的bitmap时候,draw方法可能并不耗时,但却会导致卡顿

bitmap的绘制需要上传纹理到gpu可能导致耗时

Path导致:

ClipPath(Path) 会触发昂贵的裁剪操作,因此也需要尽量避免。在可能的情况下,应该尽量直接绘制出需要的形状,而不是裁剪成相应的图形;这样性能更高,并且支持反锯齿

Savelayer导致:

如果在一个支持硬件加速的Canvas上调用 Canvas.drawPath(), 系统会首先在CPU上绘制这些path,然后把它传递给GPU。如果你的path对象很大,那最好避免在每一帧之间修改它,这样path对象就可以被系统缓存起来,使得绘制更加高效。

6.总结:

掉帧可以分为3种原因:UI线程绘制帧时间过长渲染线程耗时过长UI线程任务调度延迟 通过systrace可以比较容易的分析后前个原因,但是对于 UI线程任务调度延迟 可以需要结合traceview来使用,同时还可以往代码里添加自定义的trace位置,

附加谷歌的介绍:

https://zhuanlan.zhihu.com/p/27065828