1概述
以 密卷点评中的标记功能为例,
优化前后的帧率对比
可以看到大约 15 帧的提升。
2 分析方法
2.1 打印帧率
评估一个应用的流畅度,除了主观感受外,定量的帧率测量是很有必要的。从4.4 之后都支持通过设置属性来打开帧率输出。
具体操作:
setprop debug.sf.fps 1
stop;start(5.0 以后不需要,但是需要退到主界面滑动,直到下述 log 输出帧率)
logcat –s SurfaceFlinger 观察帧率
从手写标记这个应用中可以看到,原始的帧率在 20 帧左右
2.2 瓶颈分析
分析瓶颈,我们可以利用谷歌提供的 DDMS 工具进行。首先我们使用 systrace 获取几项图形系统相关的数据。
具体操作:
执行 adb root,确保 adb 具有 root 权限
点 击OK之 后 , 开 始 进 行 手 写 操 作 , 采 集 时 间 到 了 之 后 , 会 在
使用 chrome 浏览器打开该文件
查看被测进程
测量 draw 时间
可以看到一帧的绘制时间在 45ms 左右,排除 input 派发等其他操作,折算一下,帧率大约就是1000ms/45ms≈20fps,超出了 60fps 下所要求的一帧 16ms。可以认为,draw 是瓶颈所在。
2.3 分析热点函数
热点函数我们同样使用 DDMS 自带的 Method Profiling 工具
具体操作:
开始滑动,采集数据
停止采集。
这里建议在点击 start 之前就开始滑动,因为系统默认只能采集 8MB 的数据,超过 8M 是不会继续采集的。
展开之后,不需要使用列表中的统计功能,已经能够看到几个耗时的区块
点击几个色块,可以看到,热点函数集中在
其中以下的红框调用是需要分析的
做减法评估大头
om.eebbk.themeplayer.ScrollViewLinearLayout.drawCacheBackground(Canvas)----去掉,提升 5帧
com.eebbk.themeplayer.ScrollViewLinearLayout.drawDrawingStroke(Canvas)--------去掉,提升 10帧
之后会出现一个问题,去掉上述函数调用之后,仍然没有办法达到 60 帧的满帧。因此将注意力集中到
至此,工具已经没有办法提供更多的有用信息。
2.4 分析绘制代码
打开 draw 相关 log 开关
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -110,7 +110,7 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean DBG = false;
private static final boolean LOCAL_LOGV = false;
/** @noinspection PointlessBooleanExpression*/
- private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
+ private static final boolean DEBUG_DRAW = true || LOCAL_LOGV;
查看 log,可以发现
Invalidate child: Rect(118, 170 - 1536, 1952)
脏区非常大,也就是每次需要重绘的区域非常大。
对比满帧的草稿纸应用,Invalidate child: Rect(532, 1004 - 547, 1023)这个脏区就很小。
继续在 invalidate 的路径上(函数 invalidateInternal)添加 log
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11803,6 +11803,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
+ //debug();
+ Log.e("cw","View:" + this + " dirty {" + l + ", " + t + ", " + r + ", " + b + "}", new
Throwable());
p.invalidateChild(this, damage);
}
对 比 密 卷 和 草 稿 纸 两 个 应 用 , 可 以 发 现 草 稿 纸 调 用 了
com.eebbk.draftpaper.slate.Slate.invalidate(Rect),是带矩形参数的,因此脏区可以控制的很小。
而密卷调用的是com.eebbk.themeplayer.ScrollViewLinearLayout.drawStroke(Bitmap, Bitmap, int,
int)中的 postInvalidate,不带参数会导致整个 ScrollViewLinearLayout 重绘。
3 应对方法
根据上述分析,瓶颈在于 draw 的脏区过大,导致重绘耗时。因此至少可以有两处优化:
可以参照草稿纸应用,减小脏区
将 com.eebbk.themeplayer.ScrollViewLinearLayout.drawCacheBackground(Canvas)做适当优化,避免每
次 draw 都重新绘制这个静止的背景。