Android性能优化案例学习-第二部

来源:互联网 发布:linux vi中搜索字符串 编辑:程序博客网 时间:2024/04/19 18:13

这是Google的Android开发工程师Romain Guy刊登在个人Blog上的一篇文章。

Romain Guy 曾是Android图形渲染和系统优化的专家,是Android 4.1中的“黄油项目”开发者之一,曾多次在Devoxx,Google I/O等技术大会上进行演讲。

最近我在项目中解决的一个性能问题深受这篇文章影响,所以进行了个人翻译,希望能对Android开发者带来一些借鉴。

--

两年前,我发表了一篇博客《Android性能优化案例研究》(原文,中文翻译),详细的介绍了一些工具和技术来帮助Android开发者去定位、跟踪和解决性能问题。

这篇博客是以Falcon Pro(Joaquim Vergès设计和开发的一款Twitter客户端)做为例子。Joaquim很欢迎我在我的这篇博客中使用他这款应用作为例子,并且很快的定位出了我发现的这些问题。直到最近Joaquim主动联系了我,希望我能帮助他定位在他用scratch开发的新应用Falcon Pro 3中影响列表滑动流畅性的性能问题。同样的,这次我也没有看过源码。

Joaquim使用上次介绍过的工具快速的排除了一些这次产生性能问题的原因。譬如,他这次没有发现过度重绘的问题。最后将问题的原因定位在ViewPager控件的使用上。他给我发来了下面的屏幕截图:

图1

Joaquim使用了开发者工具中的”GPU呈现模式分析”工具来检查应用的掉帧。图1中是在打开了”GPU呈现模式分析”选项后,左右两边屏幕截图显示了在首页中没有和有ViewPager时ListView滚动的帧率情况(图1中的绿线代表了60帧帧率,超过了绿线越多就有更多掉帧的可能,导致了滑动中的不流畅)。罪魁祸首已经很明显了。

我首先想到的就是这个ViewPager是否错误的使用了hardware layer(hardware layer能够使view通过GPU以更有效的方式渲染,但会带来一点额外的开销)。我们观察到的问题有可能是因为在ListView滚动的每一帧都有view在更新自己的hardware layer所导致。但是在打开开发者工具中的”显示硬件层更新”工具后,我们并没有发现什么异常情况。我又通过HierarchyViewer检查了一遍,最终我很满意这个ViewPager在这方面上的良好表现。

然后我转向使用一个强大、鲜有使用的工具Tracer for OpenGL来看看能不能发现点线索。上一篇博客《Android性能优化案例研究》(原文,中文翻译)介绍了这个工具如何使用。简单的来说就是这个工具会手机会收集所有从UI组件发到GPU的drawing command。

Android4.3及以后:Tracer因为我们在Android4.3引入了drawing command的重排与合并后变的难读了一些。这是一项十分有用的优化,但是无法是Tracer通过view来分组drawing command。你可以通过以下命令来禁止使用此项优化来回到旧版的使用方式(在应用启动前运行)。

adb shell setprop debug.hwui.disable_draw_reorder true

如何看OpenGL traces:蓝色的命令行是向屏幕绘图的GL操作。其他的命令是进行数据转换或状态设置的,可以轻易的忽略。当你点击蓝色的命令行时,Tracer会在右边的tab中显示当前正在渲染的目标,然后你就可以通过一行一行的点击蓝色命令行构建出你获取的每一帧。我就是通过这种方式使用Tracer for OpenGL的,通过观察每一帧是如何渲染出来的,来得知应用正在做什么。

在我仔细查看Listview滑动时收集到的每一帧数据时,我意外的发现了大量的SaveLayer/ComposeLayer命令块。

图2

这些命令块表明了一个临时hardware layer的创建和构成。Canvas.saveLayer()的一些变异体创建了这些layer。当View的alpha值<1(参考View.setAlpha())时且满足下面列举的条件时,UI组件通过Canvas.saveLayer()来画Views。

  • getAlpha() 返回值 < 1
  • onSetAlpha() 返回 false
  • getLayerType() 返回 LAYER_TYPE_NONE
  • hasOverlappingRendering() 返回 true

我和Chet曾经解释过多次为什么要慎重的设置View的alpha值。每次UI组件都必须使用临时的layer,drawing command会被发送给不同的渲染对象,切换渲染对象会使GPU做大量的工作。这种使用方法会影响GPU的性能表现当这个GPU使用tiling/deferred的架构时(譬如ImaginationTech’s SGX, 高通的Adreno等GPU)。NvidiaGPU的
直接渲染架构(Direct rendering architectures)则会有更好的表现。因为Joaquim和我正在使用的Moto X 2014都是使用的高通的Adreno GPU,所以确定了这些数量众多的临时hardware layer就是导致这次性能问题的原因。

那现在问题就变成了谁产生了这些临时的layer?我们从tracer中得到了答案。图2中显示了出来,在这堆SaveLayer的drawing command右侧的detail页中显示了一个小圆圈。现在我们来从应用截图中找找这个小圆圈的位置。

图3

很好找吧,顶部的那些小圆圈。这些圆圈时ViewPager的页标,表明了当前用户正在看的位置。Joaquim用了一个第三方控件来做到这个效果。令人呵呵的是这个控件竟然用下面这种方式实现了这个功能:用白色的小圆圈表示当前页,其他页用看起来像灰色的圆圈来表示。这其中看起来像灰色的意思其实是拿白色圆圈设置透明度来实现的颜色变化。

原因找到了,通过一下方法可以解决这个问题:

  • 用灰色圆圈代替在view上设置透明度。
  • 继承hasOverlappingRendering()方法并返回false。这样做会使Framework会为你在该View 的Paint上设置一个合适的alpha值。
  • 继承onSetAlpha() 方法并返回true。这样做使你避免了Framework的处理,由你自己在onDraw()方法中通过设置Paint的alpha值来画你的灰色圆圈。

最简单的解决方案就是第二种,但是hasOverlappingRendering()函数只能在Android 4.1 Jelly Bean (API level 16)之后使用。如果你必须支持旧版本的话,就只能使用另外两个方案了。我觉得Joaquim直接自己写一个就好了。

我希望这篇文章能够说明很多性能问题都是由一些无辜无害的操作引发的。所以别假设直接上工具。

--

感谢Romain Guy。

0 0
原创粉丝点击