Android 的屏幕滚动操作不如 iPhone 流畅跟手,是什么原因导致的

首先, 安卓不缺硬件加速的支持。从安卓 1.0 开始,安卓就支持硬件加速。菜单的显示/隐藏、提醒的滑动渐变、Activity 之间的过渡以及对话框的显示/隐藏等都是经过硬件加速的。

但硬件加速与安卓「窗口」的概念有关。比如以下这张截图,就包含四个窗口:


状态栏窗口 / 壁纸窗口 / 壁纸上的启动器窗口 / 菜单窗口

安卓一开始的设计目标是「提供开放的应用平台」,在这种设计思路下,安卓通过许多个独立的 UI 元素来分享屏幕:比如输入法窗口和应用窗口就是两个不同的窗口。而同一个安卓应用,也由许多 Activity 组成(比如联系人列表是一个 Activity,联系人详情是另一个 Activity),每个 Activity 又有自己的窗口。发现了吗? 安卓 UI 一切皆窗口:从主屏打开联系人应用,你看到的是主屏窗口和联系人列表窗口的动画;按下联系人查看详情,是联系人列表窗口和联系人详情窗口的动画;显示输入法,是键盘窗口的动画.. 早期的安卓使用软件来渲染窗口内的内容:在 2.3 前,窗口内容由软件渲染,而窗口的组合 / 移动则通过硬件加速绘制。

自安卓 3.0 起支持「全」硬件加速,原先由 CPU 完成的窗口渲染可通过 GPU 完成了。但许多老应用的 UI 组件不支持此功能,所以在开发者菜单中打开强制硬件加速可能会有各种异常出现。

但为什么即便支持了硬件加速,也不能完全保证流畅度呢?事实上, 安卓的这个多窗口设计,意味着 GPU 需要同时支持不同进程的多个活动 GL 上下文。而即便到现在,多数移动 GPU 执行上下文切换的代价还是相当高的。

虽然安卓有堆硬件这一说,但 硬件加速的资源也很容易被安卓的渲染机制吃光。比方说,Tegra 2 足够在 60 帧下把 1280*800 屏幕的每个像素点渲染 2.5 次。但安卓 3.0 中,光是打开「所有应用」的视图,就需要绘制许多不同的窗口:需要对所有像素绘制一次背景;(往少了说)需要对一半的像素绘制一次 shortcut 和 widget 层;需要对一半的像素绘制一次图标和标签;也需要对所有像素绘制一次「所有应用」视图的黑色背景,还有「所有应用」视图的图标和标签…还不算对这些窗口做最后的组合,就把 GPU 的资源吃光了。当然,安卓对这个机制也有优化,比如把壁纸做成一个比屏幕大的窗口,这样在主屏滚屏时就不需要重绘,只要移动窗口就行。而这个绘制好了的窗口,就不需要额外的 GPU 计算量了。

另一方面,OpenGL 硬件加速绘图也不是万能的,Nexus S 和 Galaxy Nexus 中,每个 OpenGL 应用会占用 8MB 内存。要知道 2MB 的进程开支都是个不小的代价。这 8MB 内存可能从后台进程那里分配而来,造成应用切换速度的下降。

为了提升流畅度,还需要许多其他方面的努力:安卓 1.6 对前后台进程调度的优化、2.3 中对输入系统的重写、 加入并发的垃圾收集等。举一个流畅度不由硬件加速决定的例子:对滚屏操作,Nexus S 在 ICS 上的流畅度比在 2.3 中要低。这其实是因为计时机制发生了变化,有时在 ICS 中,当应用接收触摸事件并绘图时,可能在尚未准备好的情况下就获得了下一个事件,从而导致跟踪手指移动时可能错过一帧,但这时帧率仍然是 60fps (这个 bug 已经修复了)。

@Julius 提到的是关于浏览器的渲染情况。在这方面,安卓和 iOS 的主要差别并非来自硬件加速绘图。早期安卓在渲染网页时做了与 iOS 不同的折衷: 将网页以序列方式连续显示,而非贴片方式这样在滚屏和缩放时不会出现 Safari 那样的占位符,但渲染的帧率不够快。安卓 3.0 后改用了贴片方式,改善了滚屏和缩放的体验。但不论是安卓还是 iOS,贴片都是由 CPU 渲染的。

还有,「安卓后台应用太多吃资源」的说法也有问题。安卓的 UI 线程以默认优先级运行,后台线程以后台优先级运行。切换到后台的应用强制以后台优先级运行。而后台优先级利用了 Linux 的 cgroup 机制,它将所有的后台线程放进一个特别的调度组中, 它们满打满算也无法占用超过 10% 的 CPU 资源

「安卓的触摸事件不像 iOS 那样优先」的说法也是错的。你可以架梯子看看安卓进程的优先级设定
http://developer.android.com/reference/android/os/Process.html#THREAD_PRIORITY_AUDIO 

总的来说,根据 Dianne 的说法,多窗口设计对屏幕绘制的开销,是影响安卓的流畅度的已知因素之一。但决定「流畅」的因素还有很多,抓住某个特定技术细节不放的说法都是有失偏颇的。

以上。

Tagged: ,

Comments are closed.