`

Android Project Butter分析

 
阅读更多

Android Project Butter分析

一背景知识介绍

随着时间的推移,Android OS系统一直在不断进化、壮大,日趋完善。但直到Android 4.0问世,有关UI显示不流畅的问题也一直未得到根本解决。在整个进化过程中,AndroidDisplay(显示)系统这块也下了不少功夫,例如,使用硬件加速等技术,但本质原因似乎和硬件关系并不大,因为iPhone的硬件配置并不比那些价格相近的Android机器的硬件配置强,而iPhone UI的流畅性强却是有目共睹的。

Android 4.1(版本代号为Jelly Bean)开始,Android OS开发团队便力图在每个版本中解决一个重要问题(这是不是也意味着Android OS在经过几轮大规模改善后,开始进入手术刀式的精加工阶段呢?)。作为严重影响Android口碑问题之一的UI流畅性差的问题,首先在Android 4.1版本中得到了有效处理。其解决方法就是本文要介绍的Project Butter

Project ButterAndroid Display系统进行了重构,引入了三个核心元素,即VSYNCTriple BufferChoreographer。其中,VSYNC是理解Project Buffer的核心。VSYNCVertical Synchronization(垂直同步)的缩写,是一种在PC上已经很早就广泛使用的技术。读者可简单的把它认为是一种定时中断。

接下来,本文将围绕VSYNC来介绍Android Display系统的工作方式[①]。请注意,后续讨论将以Display为基准,将其划分成16ms长度的时间段,在每一时间段中,Display显示一帧数据(相当于每秒60帧)。时间段从1开始编号。

首先是没有VSYNC的情况,如图1所示:

 

 

没有VSYNC的绘图过程

由图1可知:

  •  时间从0开始,进入第一个16msDisplay显示第0帧,CPU处理完第一帧后,GPU紧接其后处理继续第一帧。三者互不干扰,一切正常。
  • 时间进入第二个16ms:因为早在上一个16ms时间内,第1帧已经由CPUGPU处理完毕。故Display可以直接显示第1帧。显示没有问题。但在本16ms期间,CPUGPU却并未及时去绘制第2帧数据(注意前面的空白区),而是在本周期快结束时,CPU/GPU才去处理第2帧数据。
  • 时间进入第316ms,此时Display应该显示第2帧数据,但由于CPUGPU还没有处理完第2帧数据,故Display只能继续显示第一帧的数据,结果使得第1帧多画了一次(对应时间段上标注了一个Jank)。
  • 通过上述分析可知,此处发生Jank的关键问题在于,为何第116ms段内,CPU/GPU没有及时处理第2帧数据?原因很简单,CPU可能是在忙别的事情(比如某个应用通过sleep固定时间来实现动画的逐帧显示),不知道该到处理UI绘制的时间了。可CPU一旦想起来要去处理第2帧数据,时间又错过了!

为解决这个问题,Project Buffer引入了VSYNC,这类似于时钟中断。结果如图2所示:

引入VSYNC的绘制过程

由图2可知,每收到VSYNC中断,CPU就开始处理各帧数据。整个过程非常完美。

不过,仔细琢磨图2却会发现一个新问题:图2中,CPUGPU处理数据的速度似乎都能在16ms内完成,而且还有时间空余,也就是说,CPU/GPUFPS(帧率,Frames Per Second)要高于DisplayFPS。确实如此。由于CPU/GPU只在收到VSYNC时才开始数据处理,故它们的FPS被拉低到与DisplayFPS相同。但这种处理并没有什么问题,因为Android设备的Display FPS一般是60,其对应的显示效果非常平滑。

如果CPU/GPUFPS小于DisplayFPS,会是什么情况呢?请看图3

3  CPU/GPU FPS较小的情况

由图3可知:

  • 在第二个16ms时间段,Display本应显示B帧,但却因为GPU还在处理B帧,导致A帧被重复显示。
  • 同理,在第二个16ms时间段内,CPU无所事事,因为A BufferDisplay在使用。B BufferGPU在使用。注意,一旦过了VSYNC时间点,CPU就不能被触发以处理绘制工作了。

为什么CPU不能在第二个16ms处开始绘制工作呢?原因就是只有两个Buffer。如果有第三个Buffer的存在,CPU就能直接使用它,而不至于空闲。出于这一思路就引出了Triple Buffer。结果如图4所示:

4  Triple Buffer的情况

由图4可知:

  • 第二个16ms时间段,CPU使用C Buffer绘图。虽然还是会多显示A帧一次,但后续显示就比较顺畅了。

是不是Buffer越多越好呢?回答是否定的。由图4可知,在第二个时间段内,CPU绘制的第C帧数据要到第四个16ms才能显示,这比双Buffer情况多了16ms延迟。所以,Buffer最好还是两个,三个足矣。

介绍了上述背景知识后,下文将分析Android Project Buffer的一些细节。

  Project Buffer分析

上一节对VSYNC进行了理论分析,其实也引出了Project Buffer的三个关键点:

  • 核心关键:需要VSYNC定时中断。
  • Triple Buffer:当双Buffer不够使用时,该系统可分配第三块Buffer
  • 另外,还有一个非常隐秘的关键点:即将绘制工作都统一到VSYNC时间点上。这就是Choreographer的作用。Choreographer是一个极富诗意的词,意为舞蹈编导。在它的统一指挥下,应用的绘制工作都将变得井井有条。

下面来看Project Buffer实现的细节。

2.1  SurfaceFlinger家族的改进

首先被动刀的是SurfaceFlinger家族成员。目标是提供VSYNC中断。相关类图如图5所示:

5  SurfaceFlinger中和VSYNC有关的类

由图5可知:

  • HardwareComposer封装了相关的HAL层,如果硬件厂商提供的HAL层实现能定时产生VSYNC中断,则直接使用硬件的VSYNC中断,否则HardwareComposer内部会通过VSyncThread来模拟产生VSYNC中断(其实现很简单,就是sleep固定时间,然后唤醒)。
  • VSYNC中断产生时(不管是硬件产生还是VSyncThread模拟的),VSyncHandleronVSyncReceived函数将被调用。所以,对VSYNC中断来说,VSyncHandleronVSyncReceived,就是其中断处理函数。

SurfaceFlinger家族中,VSyncHandler的实例是EventThread。下边是EventThread类的声明:

class EventThread : public Thread, public DisplayHardware::VSyncHandler

EventThread定义可知,它本身运行在一个单独的线程中,并继承了VSyncHandlerEventThread的核心处理在其线程函数threadLoop中完成,其处理逻辑主要是:

  • 等待下一次VSYNC的到来,并派发该中断事件给VSYNC监听者。

通过EventThreadVSYNC中断事件可派发给多个该中断的监听者去处理。相关类如图6所示:

6  EventThreadVSYNC中断监听者

由图6可知:

  • SurfaceFlingerThread派生,其核心功能单独运行在一个线程中。
  • SurfaceFlinger包括一个EventThread和一个MessageQueue对象。
  • 对象通过mEvents成员指向一个IDisplayEventConnection类型的对象。IDisplayEventConnection是一个纯虚类,它代表VSYNC中断的监听者。其实体类是EventThread的内部类Connection
  • IDisplayEventConnection定义了一个getDataChannel函数,该函数返回一个BitTube实例。这个实例提供的read/write方法,用于传送具体的信号数据(其内部实现为socketpair,可通过Binder实现进程跨越)。

EventThread最重要的一个VSYNC监听者就是MessageQueuemEvents对象。当然,这一切都是为最大的后台老板SurfaceFlinger服务的。来自EventThreadVSYNC中断信号,将通过MessageQueue转化为一个REFRESH消息并传递给SurfaceFlingeronMessageReceived函数处理。

有必要指出,4.1SurfaceFlinger onMessageReceived函数的实现仅仅是将4.0版本的SurfaceFlinger的核心函数挪过来罢了[②],并未做什么改动。

以上是Project BufferSurfaceFlinger所做的一些改动。那么Triple Buffer是怎么处理的呢?幸好从Android 2.2开始,DisplayPage Flip算法就不依赖Buffer的个数,Buffer个数不过是算法的一个参数罢了。所以,Triple Buffer的引入,只是把Buffer的数目改成了3,而算法本身相对于4.0来说并没有变化。图7Triple Buffer的设置示意图:

7  Layer.cpp中对Triple Buffer的设置

7所示,为Layer.cpp中对Buffer个数的设置。TARGET_DISABLE_TRIPLE_BUFFERING宏可设置Buffer的个数。对某些内存/显存并不是很大的设备,也可以选择不使用Triple Buffer

2.2  Choreographer介绍

Choreographer是一个Java类。第一次看到这个词时,我很激动。一个小小的命名真的反应出了设计者除coding之外的广博的视界。试想,如果不是对舞蹈有相当了解或喜爱,一般人很难想到用这个词来描述它。

Choreographer的定义和基本结构如图8所示:

8  Choreographer的定义和结构

8中:

  • Choreographer是线程单例的,而且必须要和一个Looper绑定,因为其内部有一个Handler需要和Looper绑定。
  • DisplayEventReceiver是一个abstract class,其JNI的代码部分会创建一个IDisplayEventConnectionVSYNC监听者对象。这样,来自EventThreadVSYNC中断信号就可以传递给Choreographer对象了。由图8可知,当VSYNC信号到来时,DisplayEventReceiveronVsync函数将被调用。
  • 另外,DisplayEventReceiver还有一个scheduleVsync函数。当应用需要绘制UI时,将首先申请一次VSYNC中断,然后再在中断处理的onVsync函数去进行绘制。
  • Choreographer定义了一个FrameCallback interface,每当VSYNC到来时,其doFrame函数将被调用。这个接口对Android Animation的实现起了很大的帮助作用。以前都是自己控制时间,现在终于有了固定的时间中断。
  • Choreographer的主要功能是,当收到VSYNC信号时,去调用使用者通过postCallback设置的回调函数。目前一共定义了三种类型的回调,它们分别是:
  • CALLBACK_INPUT:优先级最高,和输入事件处理有关。
  • CALLBACK_ANIMATION:优先级其次,和Animation的处理有关。
  • CALLBACK_TRAVERSAL:优先级最低,和UI等控件绘制有关。

优先级高低和处理顺序有关。当收到VSYNC中断时,Choreographer将首先处理INPUT类型的回调,然后是ANIMATION类型,最后才是TRAVERSAL类型。

此外,读者在自行阅读Choreographer相关代码时,还会发现AndroidMessage/Looper[③]也进行了一番小改造,使之支持Asynchronous MessageSynchronization Barrier(参照Looper.javapostSyncBarrier函数)。其实现非常巧妙,这部分内容就留给读者自己理解并欣赏了。

相比SurfaceFlingerChoreographerAndroid 4.1中的新事物,下面将通过一个实例来简单介绍Choreographer的工作原理。

假如UI中有一个控件invalidate了,那么它将触发ViewRootImplinvalidate函数,该函数将最终调用ViewRootImplscheduleTraversals。其代码如图9所示:

9  ViewRootImpl scheduleTraversals函数的实现

由图9可知,scheduleTraversals首先禁止了后续的消息处理功能,这是由设置LooperpostSyncBarrier来完成的。一旦设置了SyncBarrier,所有非Asynchronous的消息便将停止派发。

然后,为Choreographer设置了CALLBACK类型为TRAVERSAL的处理对象,即mTraversalRunnable

最后调用scheduleConsumeBatchedInput,这个函数将为Choreographer设置了CALLBACK类型为INPUT的处理对象。

ChoreographerpostCallback函数将会申请一次VSYNC中断(通过调用DisplayEventReceiverscheduleVsync实现)。当VSYNC信号到达时,Choreographer doFrame函数被调用,内部代码会触发回调处理。代码片段如图10所示:

10  Choreographer doFrame函数片段

ViewRootImpl来说,其TRAVERSAL回调对应的处理对象,就是前面介绍的mTraversalRunnable,它的代码很简单,如图11所示:

11  mTraversalRunnable的实现

doTraversal内部实现和Android 4.0版本一致。故相比于4.0来说,4.1只是把doTraversal调用位置放到VSYNC中断处理中了。

通过上边的介绍,可知Choreographer确实做到了对绘制工作的统一安排,不愧是个长于统筹安排的“舞蹈编导”。

三总结

本文通过对Android Project Butter的分析,向读者介绍了VSYNC原理以及Android Display系统的实现。除了VSYNC外,Project Butter还包括其他一些细节的改进,例如避免重叠区域的绘制等。

简言之,Project Butter从本质上解决了Android UI不流畅的问题,而且从Google I/O给出的视频来看,其效果相当不错。但实际上它对硬件配置还是有一定要求的。因为VSYNC中断处理的线程优先级一定要高,否则EventThread接收到VSYNC中断,却不能及时去处理,那就丧失同步的意义了。所以,笔者估计目前将有一大批单核甚至双核机器无法尝到Jelly Bean了。

 


 

[①]1到图4,请参考2012 Google I/O大会的宣传资料

[②]关于SurfaceFlinger的代码详解,请参考《深入理解Android I》第8

[③]关于MessageQueueHandler,请参考《深入理解Android 2》第2章以及《深入理解Android I》第5

分享到:
评论

相关推荐

    android显示系统黄油计划

    Project Butter 对 Android Display 系统进行了重构,引入了三个核心元素,即 VSYNC、Triple Buffer 和 Choreographer。其中,VSYNC 是理解 Project Buffer 的核心。VSYNC 是 Vertical Synchronization(垂直同步)...

    Android 4.1.2 SDK平台工具集

    2. **Project Butter**:Android 4.1引入了Project Butter,这是一个优化触摸响应速度和整体系统流畅性的项目。通过将帧率提高到60fps,减少了动画和用户界面操作中的延迟,显著改善了系统的平滑度。 3. **...

    Android4.2(Android17)的源码

    5. **Project Butter**:为提升触摸响应速度和流畅性,Android 4.2推出了Project Butter,通过VSYNC同步帧率和GPU渲染,使得系统动画更加平滑。源码中包含了许多与之相关的优化技术。 6. **通知中心改进**:4.2的...

    Android源码分析

    Android的构建系统AOSP(Android Open Source Project)使用了Gradle作为构建工具。分析源码构建过程,可以理解依赖管理、版本控制和自动化的构建流程,这对于优化项目构建速度和自动化部署有着重要意义。 总之,...

    Android studio 小案例

    - Android Studio拥有丰富的插件生态系统,如Butter Knife(视图注入)、GsonFormat(JSON到Java对象转换)、Material Design Icon Generator(生成Material Design图标)等,可扩展其功能。 通过这个"Android ...

    android4.2源码

    2. **Project Butter**:在Android 4.2中,Google引入了Project Butter,旨在提高系统的流畅性。通过三倍缓冲技术、VSYNC同步和硬件加速,使用户界面更加顺滑。 3. **多用户支持**:Android 4.2首次引入了多用户...

    Android SDK (SDK Platforms)-android-16.zip

    1. **Project Butter**:提升了系统的流畅性,通过提高帧率和优化渲染机制,使用户界面更加平滑。 2. **Voice Search**:增强了语音搜索功能,用户可以直接通过语音与设备交互。 3. **Data Usage Monitor**:提供...

    android4.1sdk 16

    1. **Project Butter(黄油计划)**: Android 4.1的核心改进之一是提高了系统的流畅性,目标是实现60帧每秒的刷新率。Project Butter通过优化硬件加速、触摸响应和系统渲染,显著提升了用户体验。 2. **Google Now*...

    androidsdk16.rar

    它带来了许多性能改进和新特性,如Project Butter的引入,显著提升了用户界面的流畅性。此外,该版本还优化了通知系统、语音搜索功能,并增强了无障碍服务。 二、Android模拟器 Android模拟器是Android SDK的一...

    android-17官网Demo

    - Android 17进一步优化了UI流畅性,通过Project Butter实现了60fps的平滑滚动,显著提升了系统的响应速度和触控体验。 5. **Android Studio集成**: - 虽然在Android 17时期,Eclipse还是主要的开发工具,但随着...

    android sdk 4.2.2源码包

    1. **Project Butter**: 为了提升用户体验,Android 4.2.2引入了Project Butter,这是一个优化帧率和触控响应的机制,目标是提供60fps的流畅界面。 2. **多用户支持**: 在4.2.2中,设备首次支持多个用户账户,每个...

    Android SDK android-16_r05.zip

    其中最显著的提升是Project Butter,它优化了触控体验,使得系统界面更加流畅,帧率稳定在60fps,提升了用户体验。此外,还引入了Google Now,一个智能个人助手,能提供实时信息和建议,如天气预报、交通信息等。 ...

    android classes.jar 4.0-5.0

    2. `classes4.1.1.jar`:代表Android 4.1.1(Jelly Bean)的类库,该版本增强了性能和用户体验,引入了Project Butter流畅度提升。 3. `classes4.3.jar`:Android 4.3也是Jelly Bean系列,增加了对OpenGL ES 3.0的...

    android_sdk.zip_android

    1. **Project Butter**: 为了提升用户体验,4.1引入了Project Butter,实现了60fps的平滑滚动和动画,显著提高了系统的流畅度。 2. **Google Now**: 首次引入的智能个人助手,可以提供实时的、情境感知的信息,如...

    android4.1API

    - **Project Butter(黄油项目)**: Android 4.1的一大亮点就是Project Butter,它显著提升了系统的流畅度,通过提高触控响应速度和帧率,使用户界面更加顺滑。 - **Voice Search(语音搜索)**: 提供了更强大的...

    androidSDK19源码

    2. **Project Butter**:在Android 4.4中,Project Butter进一步提升了触控体验,通过硬件加速和帧率同步技术,使UI操作更为平滑,达到了每秒60帧的刷新率。 3. **ART(Android RunTime)**:从KitKat开始,Android...

    android-16

    1. **Project Butter**: Android 4.1引入了Project Butter,这是一个优化触摸体验的项目,通过提高帧率至60fps,使得用户界面更加流畅和响应迅速。 2. **Google Now**: Google Now是Android-16的一大亮点,它提供了...

    Android 4.1.2 源码

    5. **UI框架**:Android 4.1.2引入了Project Butter,提升了触摸响应速度和流畅度。源码中可以看到触摸事件处理、动画系统、布局管理等方面的内容。 6. **应用程序框架**:包括Intent机制、BroadcastReceiver、...

    android 4.1.2 jb源码

    - **Project Butter**:这是Android 4.1引入的一项重大改进,目标是提高系统流畅性,通过硬件加速和VSYNC同步达到60帧/秒的动画效果。 - **Notification Enhancements**:通知栏得到了增强,允许更丰富的通知样式,...

    Android介绍英文ppt课件.ppt

    例如,4.0 Ice Cream Sandwich因其整合平板和手机特性而广受欢迎,4.1 Jelly Bean及其后的版本通过Project Butter提升了用户体验。 除了技术进步,Android还致力于提供一个安全、可扩展的生态系统。Android Market...

Global site tag (gtag.js) - Google Analytics