UI 性能优化的核心思路是在主线程做尽可能少的工作。具体做法是先做 profiling,找到性能瓶颈,设法优化掉,再从头重复这个过程。彩票项目中竞彩足球的投注页面使用了 ListView,在优化前非常卡顿,用户怨声载道。使用 Traceview 做 profiling 后发现单次 getView 调用耗时超过750ms。这里面暴露出了第一个性能瓶颈:Html.fromHtml(String source)。因为有一些富格式文本(比如球队排名要用红色小字)需要显示,优化前的做法是直接拼接出一个 HTML 字符串,扔给这个方法处理。这样做引入了额外的解析 HTML 文本的开销(事实证明这个开销很大),而这完全是不必要的,可以直接使用 SpannableString 来指定文本格式。优化掉这个瓶颈后发现了第二个瓶颈 TextView.setText,其单次调用耗时最高达到了数百毫秒,而一次典型的 getView 调用可含有10次对它的调用。为什么一个简单的设置文字的操作会如此耗时?后来发现在视图宽、高不固定的情况下,setText 会导致重新布局(因为文字长度变了),而这往往会一层一层的传递上去(除非某个父视图的宽、高是固定的),导致整个父视图重新布局。在视图树结构复杂的情况下,重布局的开销非常大。在优化之前,竞彩足球所有玩法用的是同一个布局文件,这导致其非常臃肿,视图层级很深,重布局代价高昂。为此我们将不同玩法的布局文件拆开,针对每一种玩法尽可能精简视图的层级。做完这个工作后,列表的滑动变得非常顺滑,卡顿感几乎消失。让我们看看优化后的 timeline panel:
单次 getView 调用耗时约100ms,仅为优化前的七分之一。第二个瓶颈的优化体现了一点:代码可维护性的提升往往同时带来性能的提升。 comments powered by Disqus