片头声明: 1、本片是据Romain Guy剧本编写Android Performance Case Study衍生的电影,某些部分可能由于个人英语水平有限及理解原因,可能有别于原作者的原意。如有发现,请指正。以利于我们共同学习,共同进步。 2、本片是继Android性能优化案例研究(上) by孙立出的下版。狗尾续貂,望大家海涵。 各位看官,下面就接上部开播: 进一步的减少overdraw: 【Activity名字:当你打开应用后,logcat将显示包名和Activity名字。根据这个你就可以晓得在Tracer for OpenGL中要输入什么东东了。】 |
当应用已经打开并运行,使可用前两个选项: * Collection Framebuffer contents on eglSwapBuffers() * Collection Framebuffer contents on glDraw*() 第一个选项对于快速找到你感兴趣的frame十分有用,然而第二个选项则允许我们通过绘制命令查看每个frame生成使用的命令。第二个选项是解决overdraw 问题的关键所在。 开启这两个选项之后,我开始滑动主界面的时间轴。它将花费相当长的时间来获取每一个frame(不出意外的话,需要30秒)。因此我建议你下载我获取的文件(http://goo.gl/yPjB5)。你可以在Tracer for OpenGL中点击第一个按钮打开这个文件。 加载完毕后,视图展示你每一个发送到GPU的GL命令为一个frame快照。如果你下载我的文件。跳到21Frame处。当一个frame被选中之后,你可以在Frame Sunmmary tab 页中查看它是什么样子的。另外,你可以点击绘制命令,将其蓝色高亮,在Details tab页中查看这个frame的当前状态。 【总结:GL 命令被通过View分组。他们重新创建你在HierarchyViewer或者xml Layout文件夹可以看到的树。这使它很容易就能理解什么View产生了一个什么样的特定操作。】 通过连续点击前三个绘制命令,你可以看出已经在Photoshop中确定的问题,一个全屏的背景被绘制了三次。 我们通过查看下载的Tracer,可以发现更多可以优化的地方。当一个tweet(listitem)被绘制,一个ImageView被用于绘制头像(原文是:avatar)。ImageView第一次绘制了一个背景然后绘制头像(avatar)自身。 如果你仔细看,你将会发现背景只是作为图片的一个边界。这意味着,这意味着在头像图片中间的这黑色部分被 overdraw了。这片9path已经被头像图片(avatar)完全覆盖了。
|
有趣的是,同样的问题也出现在了内联媒体(inline media)中。头像图片(Avatars)图片十分小,所以他们overdraw不会导致大的消耗。但是内嵌媒体(inline media)可占据屏幕相当大的一部分区域。问题的修复与上面的方法一致。 【深度优化:我更希望Android的2d渲染管道有能力自动正确的为你overdraw。我们已经有了些想法,但是我还不能做出任何关于这方面的承诺。正像加入GPU优化,这只能作用于不透明的元素。】 扁平化视图( Flattening the view hierarchy):好的,现在我们已经对overdraw(大部分都是)关心过了,现在让我们回过头来再看看Hierarchy viewer。通过检查图 像树,我们能试图确定不需要的Views。移除Views特别是ViewGroup,这不仅可以提高帧速率,而且还可以减少内存消耗 ,启动时间等等。 快速查看Falcon Pro视图层级,你可以确定有几个ViewGroup和一个单独的子视图。这些ViewGroup一般都是不需要并且 很容易移除掉。下面展示的图片中至少最后面的两个节点是可以移除的。 也有一些另外的视图可以从这个树中移除。比如,每一个tweet都包含了一个命名为listElementBottom的RelativeLayout。这个Layout包含了作者的名字,他的Twitter,从tweet发送出来之后已经过去的时间。名字和handle是 两个分离的TextView代替了单独一个TextView加入多种类型不同的spans,时间和icon使用一个TextView和一个 ImageView可以被结合称一个单独的TextView,查看TextView's compound drawables 左边的菜单使用了多个LinearLayout+TextView+ImageView的组合来展示标签和图标。其实每个可以使用单独一个TextView就可以代替了。 |
关于“输入”事件:记不记得当我们查看systrace并且发现,当处理触摸事件的时候会有些卡顿?现在,是时候来追踪这个 问题了,traceview是我们去明晰系统正在做什么的最好工具。 Traceview是一个记录应用在调用某个方法花费多少时间的虚拟机分析器。可以在ADT的DDMS视图或者monitor中启动他,在Devices tab中选择你应用进程,然后点击Start method profiling按钮。红色环绕的三个箭头。 在开启追踪之后,我来回滑动主时间轴,并且重新点击按钮来结束trace。你也可以下载我的追踪记录: 结果如下图截屏所示。 点击第#21,ViewRootImpl.draw().高亮绘制时间。表格中最后一行给你一个建议关于如何平均花在这个方法和起子方法的时间。如你使用高亮来仔细看这时间轴,你将会注意到在连续frames之间的间隙。 一个最简单找出这些在这些间隙中到底发生了什么方法就是(选择他们其中一个的开始部分进行放大,然后点击你能发 现的最大色块)你追踪父子链(parent chain)直到找到你可以辨识出来的东西。在我的例子中,我追踪一个花费了平 均时间的0.5ms的叫Patter.compileImpl,所有都指向了DBListAdapter.bindView。 显然每次一旦有新的item被绑定或者滑动主时间轴时,应用都会重新一遍一遍的编译这相同的正则表达式。Traceview展示了绑定一个View平均花费38ms并且有56%的时间花费在解析HTML文本上。这看起来某些东西可以被可以在后台实现而不 是阻塞UI线程,正则表达式不应该每次都要重新编译。 接下来看你的了! 我留下最后一个trace作为练习。这个应用在两边有两个菜单,可以通过敲击时间线的左边或者右边来显示。(在显示这 菜单时GPU overdraws 高亮了过度绘制的部分,我已经使用Tracer for OpenGL来获取了这个问题的几帧。下载我的trace然后看看你是否能找到什么导致了overdraw(作者提示go to frame #34)。 提示就不翻译了: Hints: the application should use hardware layers by calling View.setLayerType() to simplify drawing. There are also extraneous backgrounds that can be optimized away with clever use of 9-patches. Clipping could also be very helpful. Finally, maybe a ColorFilter set on a Paint passed to setLayerType() could help remove the last drawing command.
【后语:最好使用Monitor,鄙人在使用ADT的时候老出错。 |