深入剖析android新特性 笔记
9.3 Project Butter 黄油计划
Android4.1 Jelly Bean引入了ProjectButter
先说背景,再讲解为什么ProjectButter能提升流畅性
用户感受到流畅性在于自己的输入事件与返回结果之间的延迟,若事件延迟短,则跟手,流畅,
这只是用户的角度看问题,系统中,从事件输入到最终结果响应,过程非常复杂:
(Activity)Event->SetPropertyValue->Invalidate->Measure&Layout->PrepareDraw ->
(SF)DequeueBuffer
(Activity)UpdateDisplayList->DrwaDisplayList->SwapBuffers ->
(SF)EnqueueBuffer,
(SF)CompositeWindows->PostBuffer
Acitivity进程内,主要是接收到事件后进程控件属性的更新,然后根据新的属性重新测量和布局,
布局完成后从SF获取一个缓冲,然后将新空间树的结构更新到这个buffer中,最后交给SF合成和显示。
下节讲SF,现在只要知道,SF是系统中专门负责绘制UI的系统服务就可以。
9.3.1 FPS
FramesPerSecond,FrameRate,Hz,
平稳的60FPS就算是流畅,但ipad pro 2017的refreshRate已经达到120Hz。
平稳是指,不可以有卡帧、掉帧的情况
1000ms / 60 = 16.67ms
每一帧必须保证在这个时间内处理完
Jank的产生:
VSync Vsync
Disp 0 | 1 | 1 | 2 | 3 |
GPU 1 | |2 |3 4 |
CPU 1 | 2 | 3 | 4 |
CPU负责测试和布局的计算
GPU负责图像的合成
Display是最终的显示模块,代表用户看到的结果
所有界面的刷新都要经历这三个模块的流水线作业,
流程:
开始没画面,disp0,
CPU产出1st帧的内容,然后交给GPU,接着在Disp上显示
假设CPU忙其他事,没能连续产生2nd帧内容,则导致一系列延迟,第一帧画面停留了2个VSync
ProjectBuffer引入两个机制提升流程性:
1,VSYNC机制
2,Triple Buffer
9.3.2 VSYNC
Vertical Synchronization,垂直同步
用来防止Tearing撕裂,
视频、游戏由一幅幅图像组成,称为帧
每帧有很多像素点
显示器显示每一帧画面时,需要一行一行刷新到屏幕,(逐行扫描)
显示器通过GPU的Buffer拿到要显示的每一帧数据,
假设显示器将当前帧内容刷新到一半时,来了新的一帧数据,两帧内容一起显示,Tearing。
VSYNC的目的就是避免这种情况,它告知GPU等到屏幕内容刷新完再加载下一帧画面内容,避免了Tearing
Android之前的版本已经使用VSYNC避免Tearing,jellyBean对VSYNC进行了加强,
所有显示组件都以VSYNC信号为基准来保证步调一致,
CPU收到VSYNC,产生帧数据
交给GPU处理
display出来
9.3.3 Choreographer与VSYNC
编舞者
Choreographer是伴随ProjectButter新增的API,
负责统一动画、输入、绘画的时机。
负责接受VSYNC信号然后安排下一帧所需的相关工作
开发者可以通过Choreographer.postFrameCallback(FrameCallback c)API来提交一个回调,这个回调会在下一帧时刻执行。
每个Looper线程都有自己的Choreographer,
其他线程可以发布回调,以在Choreographer运行,但他们将在Choreographer所属的Looper上运行。
这个有结构图,分java层,C++层,
图的结构说明:
Choreographer提供FrameCallback,来让开发者实现帧渲染监听的回调,Choreographer负责调度这些回调,
<<interface>> <---------- android.view.Choreographer
FrameCallback +getInstance():Choreographer
+doFrame() : void +scheduleVsyncLocked() : void
+doFrame() : void
+doCallbacks() : void
//依赖关系,Choreographer只有传入了FrameCallback才有用
FrameDisplayEventReceiver是DisplayEventReceiver的子类;
android.view.DisplayEventReceiver <------ FrameDisplayEventReceiver
+nativeScheduleVsync() +scheduleVsync()
+scheduleVsync()
+onVsync()
+onHotplug()
-dispatchVsync()
-dispatchHotplug()
//+是public,-是private
FrameDisplayEventReceiver与android.view.Choreographer是聚合关系,强调整体包含部分,
Choreographer包含FrameDisplayEventReciver
以上是JAVA层
DisplayEventReceiver通过JNI与native端的NativeDsiplayEventReceiver连通,
NativeDisplayEventReceiver是DisplayEventDispatcher的子类;
DisplayEventDispatcher <------- NativeDsiaplyEventReceiver
-handleEvent() +scheduleVsync
+processPrendingEvents() -dispatchVsync()
-dispatchVsync() -dispatchHotplug()
-dispatchHotplug()
+scheduleVsync()
DisplayEventDispatcher包含了一个DisplayeventReceiver对象
DisplayEventReceiver
+requestNextVsnc()
+getEvents()
+getFd()
DisplayEventReceiver包含IDisplayEventConnection与BitTube
IDisplayEventConnection
+getDataChannel() : BitTube
+setVsyncRate(count:int) : void
+requestNextVsync() : void
BitTube
+recvObjects()
+sendObjects()
+getFd():int
DisplayEventReceiver可以获取到显示时间的接收器,包含了VSYNC事件,
所以,VSYNC事件传递流程:
BitTube => DisplayEventReceiver => DisplayEventDispatcher =>
NativeDisplayEventRevicer => DisplayEventReceiver =>
FrameDisplayEventReceiver => Choreographer
VSYNC事件的接收
上图还有IDisplayEventConnection和BitTube类没介绍,他们与接收VSYNC事件密切相关,详细看下
DisplayEventReceiver包含了IDisplayEventConnction与BitTube两个类型的指针:
//DisplayEventReceiver.h
sp<IDisplayEventConnection> mEventConnection;
std::unique_ptr<gui :: BitTube> mDataChannel;
并在DisplayEventReceiver构造函数中对他们初始化,
//DisplayEventReceiver.cpp
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource){
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if(sf != NULL) {
mEventConnection = sf -> createDisplayEventConnnection(vsyncSource);
if(mEventConnnection != NULL){
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
}
}
}
ISurfaceComposer在下一节讲,这里只要知道,通过sf->createDisplayEventConnection变获取到了SurfaceFlinger的连接就可以了。
Tube是通道,BitTube就是传输bit数据的通道,
VSYNC事件就是通过这个通道从SF传递到位于AP进程的DisplayEventReceiver中的,
数据传递的方法是通过BitTube的sendObject和recvObjects两个静态方法:
......
9.3.4 TripleBuffer