Android 之 Project Butter 详细介绍
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 之 Project Butter 详细介绍相关的知识,希望对你有一定的参考价值。
参考技术AUI 优化系列专题,来聊一聊 android 渲染相关知识,主要涉及 UI 渲染背景知识 、 如何优化 UI 渲染 两部分内容。
《 View 绘制流程之 setContentView() 到底做了什么? 》
《 View 绘制流程之 DecorView 添加至窗口的过程 》
《 深入 Activity 三部曲(3)View 绘制流程 》
《 Android 之 LayoutInflater 全面解析 》
《 关于渲染,你需要了解什么? 》
《 Android 之 Choreographer 详细分析 》
《 Android 之如何优化 UI 渲染(上) 》
《 Android 之如何优化 UI 渲染(下) 》
现在我们已经很少能够听到关于 Android UI 卡顿的话题了,这得益于 Google 长期以来对 Android 渲染性能的重视,基本每次 Google I/O 都会花很多篇幅讲这一块。随着时间的推移,Android 系统一直在不断进化、壮大,并且日趋完善。
其中,Google 在 2012 年的 I/O 大会上宣布了 Project Butter 黄油计划,那个曾经严重影响 Android 口碑的 UI 流程性问题,首先在这得到有效的控制,并且在 Android 4.1 中正式开启了这个机制。
Project Butter 对 Android Display 系统进行了重构,引入了三个核心元素,即 VSYNC 、 Triple Buffer 和 Choreographer 。
其中 VSYNC 是理解 Project Butter 的核心。接下来,我们就围绕 VSYNC 开始介绍 Project Butter 对 Android Display 系统做了哪些优化。
VSYNC 最初是由 GPU 厂商开发的一种,用于防止屏幕撕裂的技术方案,全称 Vertical Synchronization,该方案很早就已经被广泛应用于 PC 上。我们可以把它理解为一种时钟中断。
VSYNC 是一种图形技术,它可以同步 GPU 的 帧速率 和显示器的 刷新频率 ,所以在理解 VSYNC 产生的原因及其作用之前,我们有必要先来了解下这两个概念。
表示屏幕在一秒内刷新画面的次数, 刷新频率取决于硬件的固定参数,单位 Hz(赫兹)。例如常见的 60 Hz、144 Hz,即每秒钟刷新 60 次或 144 次。
逐行扫描
显示器并不是一次性将画面显示到屏幕上,而是从左到右边,从上到下逐行扫描显示,不过这一过程快到人眼无法察觉到变化。以 60 Hz 刷新率的屏幕为例,即 1000 / 60 ≈ 16ms。
表示 GPU 在一秒内绘制操作的帧数,单位 fps。例如在电影界采用 24 帧的速度足够使画面运行的非常流畅。而 Android 系统则采用更加流程的 60 fps,即每秒钟绘制 60 帧画面。更多内容参考《 Why 60 fps 》。
现在,刷新频率和帧率需要一起合作,才能使图形内容呈现在屏幕上,GPU 会获取图形数据进行绘制, 然后硬件负责把图像内容呈现到屏幕上,这一过程在应用程序的生命周期内一遍又一遍的发生。
如上图,CPU / GPU 生成图像的 Buffer 数据,屏幕从 Buffer 中读取数据刷新后显示。理想情况下帧率和刷新频率保持一致,即每绘制完成一帧,显示器显示一帧。不幸的是,刷新频率和帧率并不总是能够保持相对同步,如果帧速率实际比刷新率快,例如帧速率是 120 fps,显示器的刷新频率为 60 Hz。此时将会发生一些视觉上的问题。
当 GPU 利用一块内存区域写入一帧数据时,从顶部开始新一帧覆盖前一帧,并立刻输出一行内容。当屏幕刷新时,此时它并不知道图像缓冲区的状态,因此从缓冲区抓取的帧并不是完整的一帧画面(绘制和屏幕读取使用同一个缓冲区)。此时屏幕显示的图像会出现上半部分和下半部分明显偏差的现象,这种情况被称之为 “tearing”(屏幕撕裂)。
那如何防止 “tearing” 现象的发生呢?由于图像绘制和读取使用的是同一个缓冲区,所以屏幕刷新时可能读取到的是不完整的一帧画面。解决方案是采用 Double Buffer。
Double Buffer(双缓冲)背后的思想是让绘制和显示器拥有各自的图像缓冲区。GPU 始终将完成的一帧图像数据写入到 Back Buffer ,而显示器使用 Frame Buffer ,当屏幕刷新时,Frame Buffer 并不会发生变化,Back Buffer 根据屏幕的刷新将图形数据 copy 到 Frame Buffer,这便是 VSYNC 的用武之地。
在 Android 4.1 之前,Android 便使用的双缓冲机制。怎么理解呢?一般来说,在同一个 View Hierarchy 内的不同 View 共用一个 Window,也就是共用同一个 Surface。
每个 Surface 都会有一个 BufferQueue 缓存队列,但是这个队列会由 SurfaceFlinger 管理,通过匿名共享内存机制与 App 应用层交互。
整个流程如下:
但是 UI 绘制任务可能会因为 CPU 在忙别的事情,导致没来得及处理。所以 从 Android 4.1 开始, VSYNC 则更进一步,现在 VSYNC 脉冲信号开始用于下一帧的所有处理 。
Project Butter 首先对 Android Display 系统的 SurfaceFlinger 进行了改造,目标是提供 VSYNC 中断。每收到 VSYNC 中断后,CPU 会立即准备 Buffer 数据,由于大部分显示设备刷新频率都是 60 Hz(一秒刷新 60 次),也就是说一帧数据的准备工作都要在 16ms 内完成。
这样应用总是在 VSYNC 边界上开始绘制,而 SurfaceFlinger 总是在 VSYNC 边界上进行合成。这样可以消除卡顿,并提升图形的视觉表现。
如果理解了双缓冲机制的原理,那就非常容易理解什么是三缓冲区了。如果只有两个 Graphic Buffer 缓冲区 A 和 B,如果 CPU / GPU 绘制过程较长,超过一个 VSYNC 信号周期。
由上图可知:
为什么 CPU 不能在第二个 16ms 处理绘制工作呢?原因是只有两个 Buffer,缓冲区 B 中的数据还没有准备完成,所以只能继续展示 A 缓冲区的内容,这样缓冲区 A 和 B 都分别被显示设备和 GPU 占用,CPU 则无法准备下一帧的数据。如果再提供一个缓冲区,CPU、GPU 和显示设备都能使用各自的缓冲区工作,互不影响。
简单来说,三重缓冲机制就是在双缓冲机制基础上增加了一个 Graphic Buffer 缓冲区,这样可以最大限度的利用空闲时间,带来的坏处是多使用的一个 Graphic Buffer 所占用的内存。
由上图可知:
Choreographer 也是 Project Butter 计划新增的机制,用于配合系统的 VSYNC 中断信号。它本质是一个 Java 类,如果直译的话为舞蹈指导,这是一个极富诗意的表达,看到这个词不得不赞叹设计者除了 Coding 之外的广泛视野。舞蹈是有节奏的,节奏使舞蹈的每个动作更加协调和连贯;视图刷新也是如此。
Choreographer 可以接收系统的 VSYNC 信号,统一管理应用的输入、动画和绘制等任务的执行时机。Android 的 UI 绘制任务将在它的统一指挥下,井然有序的完成。业界一般通过它来监控应用的帧率。
Choreographer 的构造方法:
优先级的高低和处理顺序有关。当收到 VSYNC 信号时,Choreographer 将首先处理 INPUT 类型的回调,然后 ANIMATION 类型,最后才是 TRAVERSAL 类型。
另外,Android 在 4.1 还对 Handler 机制进行了略微改造,使之支持 Asynchronous Message(异步消息) 和 Synchronization Barrier(同步屏障)。一般情况下同步消息和异步消息的处理方式并没有什么区别,只有在设置了 同步屏障 时才会出现差异。 同步屏障为 Handler 消息机制增加了一种简单的优先级关系,异步消息的优先级要高于同步消息 。简单点说,设置了同步屏障之后,Handler 只会处理异步消息。
以 View 的绘制流程为例:
scheduleTraversals 首先禁止了后续消息的处理能力,一旦设置了消息队列的 postSyncBarrier,所有非 Asynchronous 的消息将被停止派发。
UI 绘制任务设置了 CALLBACK 类型为 TRAVERSAL 类型的任务,即 mTraversalRunnable:
Choreographer 的 postCallback 方法将会申请一次 VSYNC 中断信号,通过 DisplayEventReceiver 的 scheduleVsync 方法。当 VSYNC 信号到达时,便会回调 Choreographer 的 doFrame 方法,内部会触发已经添加的回调任务:
此时 UI 绘制任务 doTraversal 方法被回调,即在 Android 4.1 之后, UI 绘制任务被放置到了 VSYNC 中断处理中了。Choreographer 确实做到了统一协调管理 UI 的绘制工作。有关 Choreographer 更详细的分析,可以参考《 Android 之 Choreographer 详细分析 》。
在从根本解决 Android UI 不流畅的问题上,Project Butter 黄油计划率先迈出了最重要一步,Android 的渲染性能也确实有了很大改善。
不过优化是无止境的,Google 在后续版本中又引入了一些比较大的改变,例如 Android 5.0 的 RenderThread,Android 将所有的绘制任务都放到了该线程,这样即便主线程有耗时的操作也可以保证动画流畅性。
关于 UI 渲染所涉及的内容非常多,而且 Android 渲染框架演进的非常快,文章最后也会附上一些扩展资料,便于更好的学习理解。
文中如有不妥或有更好的分析结果,欢迎您的留言或指正。文章如果对你有帮助,请留个赞吧。
其他系列专题
Android RoboGuice开源框架Butter Knife开源框架浅析
Google Guice on Android(RoboGuice)
Dagger通过专注于一种简化的功能集以一种不同的方式达到了更好的性能。有人觉得RoboGuice节约了大量的时间。较少的代码意味着较少的错误。较少的样板代码意味着能够把很多其它的时间放到应用的核心逻辑上。所以这就是为什么我们要使用这些开源框架来开发的原因。
locManager = (LocationManager) getSystemService(Activity.LOCATION_SERVICE);的方法了,而是运用@Inject注解来实现,如:
@Inject
LocationManager locManager;
Butter Knife
class ExampleActivity extends Activity { @InjectView(R.id.user) EditText username; @InjectView(R.id.pass) EditText password; @OnClick(R.id.submit) void submit() { // TODO call server... } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.inject(this); // TODO Use "injected" views... } }
compile ‘com.jakewharton:butterknife:5.1.1‘
以上是关于Android 之 Project Butter 详细介绍的主要内容,如果未能解决你的问题,请参考以下文章
[轉]Android Libraries 介紹 - Butter knife
Android RoboGuice开源框架Butter Knife开源框架浅析
最美应用-从Android研发project师的角度之[最美时光]
Android 开源项目android-open-project解析之 GridView,ImageView,ProgressBar,TextView