Android 性能案例研究续集

发布于 2024-08-15 21:42:07 字数 3262 浏览 15 评论 0

两年前,我发表了名为 Android Performance Case Study 的文章来帮助 Android 开发者了解什么工具和技术能被应用到识别、追踪和解决性能问题上。

这篇文章的示例程序叫 Falcon Pro,是由 Joaquim Vergès 设计和开发的一个 Twitter 客户端。感谢 Joaquim 让我以 Flacon Pro 为例来处理我做演示。一切都很顺利,直到 Joaquim 开始开发 Falcon Pro 3,在发布他新的应用之前不久,Joaquim 因为需要解决一个影响滚动的性能问题他联系到了我(这一次我依然没有他的源码可以参考)。

Joaquim 使用了所有工具来检测问题所在,但是都发现与之前猜测的原因无关。比如,他发现并不是 overdraw 引发问题。于是他将范围缩小到 ViewPager 类,他发给我如图 1-1:

图 1-1

Joaquim 使用系统的 GPU 图形分析工具发现了帧速的下降。左边的截图展示了没有 Viewpage 的滚动性能时间轴,右边的展示了有 Viewpager 的时间轴(他使用 2014 年的 Moto X 获得这些数据)。这个问题根源看起来非常明显。

我的第一反应是 ViewPager 是不是误用了硬件加速。我们观察到的这个性能问题可能在 List 滚动时每一帧硬件层都被更新了。系统的 hardware layers updates debugging tool 没有提供有价值的信息。我检查了两次 HierarchyViewer, 但 ViewPager 表现令我感到满意。(相反,我认定它不太可能出现问题)

我转而使用了另一个强大的、不常用的工具: Tracer for OpenGL。我前一篇文章解释了这个工具的更多细节。这个工具收集了所有你想知道的 UI 工具发送给 GPU 的绘画命令。

Android4.3 及以前:从 Android4.3 我们引进了 reordering and merging of drawing commands 后,Tracer 变得难以使用。reordering and merging of drawing commands 是一个令人惊讶的有用优化,但是他阻止了来自 view 的组绘画命令。使用如下命令(在启动你的应用前)你可以通过关闭显示 list 优化来恢复旧的行为。

分析 OpenGl Traces:蓝色的命令显示屏幕上绘制像素的 GL 操作。其他所有被用于传输数据或者设置状态命令能很容易的被忽略掉。每次你点击蓝色的命令,Tracer 会更新 Detail 选项卡,在你点击的被执行后立即显示当前渲染对象的内容。通过一个接一个的点击,你可以重建每一帧。这几乎就是我用 Trace 分析性能问题的流程。了解被渲染的一帧都提供了什么。

当仔细看收集到的 traces 时,我惊奇的发现了一些 SaveLayer/ComposeLayer 命令块,r 如图 1-2。

图 1-2

这些块表明创建和合成了临时性的硬件层。这些临时硬件层被不同的 Canvas.saveLayer() 创建。当下面的特殊条件被满足时,UI 系统调用 Canvas.saveLayer() 函数来绘制视图,并且 alpha 值小于 1:

  • getAlpha() 返回一个小于 1 的值
  • onSetAlpha() 返回 false
  • getLayerType() 返回 LAYER_TYPE_NONE
  • hasOverlappingRendering() 返回 true

在一些演讲中,Chet 和我解释了为什么要谨慎使用 alpha 的原因 ( 译者注 : 关于 alpha 问题可以参考这篇文章 Android Tips: Best Practices for Using Alpha )。那就是每一次 UI 系统必须使用临时硬件层时,绘画命令发送给不同的渲染对象,对于 GPU 来说切换不同的渲染对象是非常耗时的操作。对于用 tiling/deferred 架构的 GPU 是一个硬伤(例如 ImaginationTech’s SGX, Qualcomm’s Adreno 等等)。直接渲染架构更好在这种情况下的表现会稍微好一些,例如:Nvidia。但是 Joaquim 和我使用的手机都是使用了 Qualcomm Adreno GPU 的 Moto X 2014 版,因此多个临时硬件层的使用可能是引起这个性能问题的根源。

更重要的问题是:是什么创建了这些临时层?Tracer 给了我们答案。如果你看到了 Tracer 的截屏,你能看到仅是一组 OpenGL 的 SaveLayer 命令在一个小的渲染对象循环调用了。现在让我们看下应用截图, 如图 1-3:

图 1-3

你看到顶部的几个小圆点了么?这是 ViewPager 指示器,用于显示使用者的页面位置。Joaquim 使用了一个第三方库来画这些指示器。当前页面的指示器是一个白色的圆点,其他页面的指示器是一个灰色的圆。我说“什么导致了它显示为灰色”,因为这些圆实际上是半透明的白色圆点。这个库中对于每一个圆都用了一个 View(这本身就是浪费)并调用 setAlpha() 改变他们的颜色。

这里有一些修复这个问题的几个解决方案:

  • 使用一个可定制的颜色来代替在 View 上设置不透明度;
  • 使 hasOverlappingRendering() 返回 false,然后系统会为你设置一个适当的 alpha 值到画笔上;(译者注 : 对于有重叠内容的 View,系统简单粗暴的使用 offscreen buffer 来协助处理。当告知系统该 View 无重叠内容时,系统会分别使用合适的 alpha 值绘制每一层。)
  • 使 onSetAlpha() 返回 true,并设置 Paint 的 alpha 来绘画灰色的圆。

最合适的是第二种方法,但最低支持 API level 16.如果你必须支持老版本,可以使用另外两个方法中的一个。我相信 Joaquim 会抛弃第三方库并使用他自己的指示器。

我希望这篇文章中市我们能够意识到性能问题很可能出现在看似无害的操作上。请记住:不要假设,实践出真理!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

我还不会笑

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

linfzu01

文章 0 评论 0

可遇━不可求

文章 0 评论 0

枕梦

文章 0 评论 0

qq_3LFa8Q

文章 0 评论 0

JP

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文