Project Butter(黄油计划)的特性,包括:java
B. 三缓冲支持,改善GPU和CPU之间绘制节奏不一致的问题;android
C. 将用户输入,例如touch event,同步到下一个Vsync信号到来时再处理;算法
D. 预测用户的touch行为,以得到更好的交互响应;api
E. 每次用户touch屏幕时,进行CPU Input Boost,以便减小处理延时。数组
随着时间的推移,Android OS系统一直在不断进化、壮大,日趋完善。但直到Android 4.0问世,有关UI显示不流畅的问题也一直未获得根本解决。在整个进化过程当中,Android在Display(显示)系统这块也下了很多功夫,例如,使用硬件加速等技术,但本质缘由彷佛和硬件关系并不大,由于iPhone的硬件配置并不比那些价格相近的Android机器的硬件配置强,而iPhone UI的流畅性强倒是有目共睹的。数据结构
从Android 4.1(版本代号为Jelly Bean)开始,Android OS开发团队便力图在每一个版本中解决一个重要问题(这是否是也意味着Android OS在通过几轮大规模改善后,开始进入手术刀式的精加工阶段呢?)。做为严重影响Android口碑问题之一的UI流畅性差的问题,首先在Android 4.1版本中获得了有效处理。其解决方法就是本文要介绍的Project Butter。app
Project Butter对Android Display系统进行了重构,引入了三个核心元素,即VSYNC、Triple Buffer和Choreographer([ˌkɒrɪ'ɒɡrəfə(r)] 编舞者)。其中,VSYNC是理解Project Buffer的核心。VSYNC是Vertical Synchronization(垂直同步)的缩写,是一种在PC上已经很早就普遍使用的技术。读者可简单的把它认为是一种定时中断。socket
接下来,本文将围绕VSYNC来介绍Android Display系统的工做方式。请注意,后续讨论将以Display为基准,将其划分红16ms长度的时间段,在每一时间段中,Display显示一帧数据(至关于每秒60帧)。时间段从1开始编号。async
首先是没有VSYNC的状况,如图1所示:ide
图1 没有VSYNC的绘图过程
由图1可知:
经过上述分析可知,此处发生Jank的关键问题在于,为什么第1个16ms段内,CPU/GPU没有及时处理第2帧数据?缘由很简单,CPU多是在忙别的事情(好比某个应用经过sleep固定时间来实现动画的逐帧显示),不知道该处处理UI绘制的时间了。可CPU一旦想起来要去处理第2帧数据,时间又错过了!
为解决这个问题,Project Butter引入了VSYNC,这相似于时钟中断。结果如图2所示:
图2 引入VSYNC的绘制过程
由图2可知,每收到VSYNC中断,CPU就开始处理各帧数据。整个过程很是完美。
不过,仔细琢磨图2却会发现一个新问题:图2中,CPU和GPU处理数据的速度彷佛都能在16ms内完成,并且还有时间空余,也就是说,CPU/GPU的FPS(帧率,Frames Per Second)要高于Display的FPS。确实如此。因为CPU/GPU只在收到VSYNC时才开始数据处理,故它们的FPS被拉低到与Display的FPS相同。但这种处理并无什么问题,由于Android设备的Display FPS通常是60,其对应的显示效果很是平滑。
若是CPU/GPU的FPS小于Display的FPS,会是什么状况呢?请看图3:
图3 CPU/GPU FPS较小的状况
由图3可知:
为何CPU不能在第二个16ms处开始绘制工做呢?缘由就是只有两个Buffer。若是有第三个Buffer的存在,CPU就能直接使用它,而不至于空闲。出于这一思路就引出了Triple Buffer。结果如图4所示:
图4 Triple Buffer的状况
由图4可知:
是否是Buffer越多越好呢?回答是否认的。由图4可知,在第二个时间段内,CPU绘制的第C帧数据要到第四个16ms才能显示,这比双Buffer状况多了16ms延迟。因此,Buffer最好仍是两个,三个足矣。
介绍了上述背景知识后,下文将分析Android Project Buffer的一些细节。
上一节对VSYNC进行了理论分析,其实也引出了Project Buffer的三个关键点:
下面来看Project Buffer实现的细节。
首先被动刀的是SurfaceFlinger家族成员。目标是提供VSYNC中断。相关类图如图5所示:
图5 SurfaceFlinger中和VSYNC有关的类
由图5可知:
在SurfaceFlinger家族中,VSyncHandler的实例是EventThread。下边是EventThread类的声明:
class EventThread : public Thread, public DisplayHardware::VSyncHandler
由EventThread定义可知,它自己运行在一个单独的线程中,并继承了VSyncHandler。EventThread的核心处理在其线程函数threadLoop中完成,其处理逻辑主要是:
经过EventThread,VSYNC中断事件可派发给多个该中断的监听者去处理。相关类如图6所示:
图6 EventThread和VSYNC中断监听者
由图6可知:
EventThread最重要的一个VSYNC监听者就是MessageQueue的mEvents对象。固然,这一切都是为最大的后台老板SurfaceFlinger服务的。来自EventThread的VSYNC中断信号,将经过MessageQueue转化为一个REFRESH消息并传递给SurfaceFlinger的onMessageReceived函数处理。
有必要指出,4.1中SurfaceFlinger onMessageReceived函数的实现仅仅是将4.0版本的SurfaceFlinger的核心函数挪过来罢了[②],并未作什么改动。
以上是Project Buffer对SurfaceFlinger所作的一些改动。那么Triple Buffer是怎么处理的呢?幸亏从Android 2.2开始,Display的Page Flip算法就不依赖Buffer的个数,Buffer个数不过是算法的一个参数罢了。因此,Triple Buffer的引入,只是把Buffer的数目改为了3,而算法自己相对于4.0来讲并无变化。图7为Triple Buffer的设置示意图:
图7 Layer.cpp中对Triple Buffer的设置
图7所示,为Layer.cpp中对Buffer个数的设置。TARGET_DISABLE_TRIPLE_BUFFERING宏可设置Buffer的个数。对某些内存/显存并非很大的设备,也能够选择不使用Triple Buffer。
协调动画,输入和绘图的时间。
Choreographer是一个Java类。第一次看到这个词时,我很激动。一个小小的命名真的反应出了设计者除coding以外的广博的视界。试想,若是不是对舞蹈有至关了解或喜好,通常人很难想到用这个词来描述它。
Choreographer的定义和基本结构如图8所示:
图8 Choreographer的定义和结构
图8中:
Choreographer的主要功能是,当收到VSYNC信号时,去调用使用者经过postCallback设置的回调函数。目前一共定义了三种类型的回调,它们分别是:
优先级高低和处理顺序有关。当收到VSYNC中断时,Choreographer将首先处理INPUT类型的回调,而后是ANIMATION类型,最后才是TRAVERSAL类型。
此外,读者在自行阅读Choreographer相关代码时,还会发现Android对Message/Looper类也进行了一番小改造,使之支持Asynchronous Message和Synchronization Barrier(参照Looper.java的postSyncBarrier函数)。其实现很是巧妙,这部份内容就留给读者本身理解并欣赏了。
相比SurfaceFlinger,Choreographer是Android 4.1中的新事物,下面将经过一个实例来简单介绍Choreographer的工做原理。
假如UI中有一个控件invalidate了,那么它将触发ViewRootImpl的invalidate函数,该函数将最终调用ViewRootImpl的scheduleTraversals。其代码如图9所示:
图9 ViewRootImpl scheduleTraversals函数的实现
由图9可知,scheduleTraversals首先禁止了后续的消息处理功能,这是由设置Looper的postSyncBarrier来完成的。一旦设置了SyncBarrier,全部非Asynchronous的消息便将中止派发。
而后,为Choreographer设置了CALLBACK类型为TRAVERSAL的处理对象,即mTraversalRunnable。
最后调用scheduleConsumeBatchedInput,这个函数将为Choreographer设置了CALLBACK类型为INPUT的处理对象。
Choreographer的postCallback函数将会申请一次VSYNC中断(经过调用DisplayEventReceiver的scheduleVsync实现)。当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了。
view.invalidate-->viewgroup.invalidateChildInParent-->...-->ViewRootImpl.invalidateChild-->ViewRootImpl.scheduleTraversals
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } void unscheduleTraversals() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals"); try { performTraversals(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_FRAME: doFrame(System.nanoTime(), 0); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); break; } } } private void postCallbackDelayedInternal(int callbackType, Runnable action, Object token, long delayMillis) { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) {//若是处理时间到了,就直接发出下一帧处理请求 scheduleFrameLocked(now); } else {// 若是处理时间未到,则或一段时间在发送请求 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } } } void doScheduleCallback(int callbackType) { synchronized (mLock) { if (!mFrameScheduled) { final long now = SystemClock.uptimeMillis(); if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) { scheduleFrameLocked(now); } } } } private void scheduleFrameLocked(long now) { if (!mFrameScheduled) {//若是没有请求过,则请求 mFrameScheduled = true; if (USE_VSYNC) { if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame on vsync."); } // If running on the Looper thread, then schedule the vsync immediately, // otherwise post a message to schedule the vsync from the UI thread as soon as possible. if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else { final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now); if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } } void doScheduleVsync() { synchronized (mLock) { if (mFrameScheduled) { scheduleVsyncLocked(); } } } // 申请一次VSYNC中断,当下一帧刷新的时候会回调mDisplayEventReceiver的onVsync(); // 而回调onVsync的线程确定非ui线程,不然的话若是MessageQueue处于阻塞状态,即ui线程处于阻塞状态确定无法运行 private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); } private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { @Override public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { mTimestampNanos = timestampNanos; mFrame = frame; Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); } @Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); } } void doFrame(long frameTimeNanos, int frame) { mFrameScheduled = false; ... doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { // We use "now" to determine when callbacks become due because it's possible // for earlier processing phases in a frame to post callbacks that should run // in a following phase, such as an input event that causes an animation to start. final long now = System.nanoTime(); callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true; ... } try { // 从头遍历到尾,执行run for (CallbackRecord c = callbacks; c != null; c = c.next) { c.run(frameTimeNanos); } } finally { synchronized (mLock) { mCallbacksRunning = false; do { final CallbackRecord next = callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null); } } }
!结合 framework下的 深刻理解控件树的绘制 的文档一块儿看!
animator 是CALLBACK_ANIMATION
Animator.start()-->AnimationHandler.scheduleAnimation private void scheduleAnimation() { if (!mAnimationScheduled) { mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null); mAnimationScheduled = true; } } // Called by the Choreographer. final Runnable mAnimate = new Runnable() { @Override public void run() { mAnimationScheduled = false; // 里面处理每一帧动画,在最后再次scheduleAnimation doAnimationFrame(mChoreographer.getFrameTime()); } };
当vsync信号到来时,Choreographer就会执行doFrame里的几个操做(input,animation,traversals),
当执行到animation类型的callback时,就会回调doAnimationFrame,在doAnimationFrame里会根据设置来改变更画值,
而后会回调监听animationUpdate,咱们通常会改变对应的属性值,并invalidate,此时就会出发一次scheduleTraversals,把这次的callback加入Choreographer对应队列数组中,
当执行完animation类型的callback后,就会执行traversals类型的callback,把traversals类型的callback一个一个的从队列中取出执行。
在doAnimationFrame最后若是动画没完,则会继续scheduleAnimation。
这就是为何animation要在traversals的前边。
// 把invalidate放在 CALLBACK_ANIMATION 中操做,
// 因此在view的绘制时若是调用了invalidate的话就会在以后的 CALLBACK_TRAVERSAL 中直接执行,而不会等到下一帧时才执行,这样的话就能充分利用这次的帧绘制时间,从而加快绘制。
public void postInvalidateOnAnimation() { // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.dispatchInvalidateOnAnimation(this); } }
// 把自定义的操做放到 CALLBACK_ANIMATION 中处理。
public void postOnAnimation(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.mChoreographer.postCallback( Choreographer.CALLBACK_ANIMATION, action, null); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); } }
不管是用纯java代码构建Animation对象,仍是经过xml文件定义Animation,其实最终的结果都是
Animation a = new AlphaAnimation(); Animation b = new ScaleAnimation(); Animation c = new RotateAnimation(); Animation d = new TranslateAnimation();
分别是透明度,缩放,旋转,位移四种动画效果。
而咱们使用的时候,通常是用这样的形式:
View.startAnimation(a);
那么就来看看View中的startAnimation()方法。
先是调用View.setAnimation(Animation)方法给本身设置一个Animation对象,这个对象是View类中的一个名为mCurrentAnimation的成员变量。
而后它调用invalidate()来重绘本身。
我想,既然setAnimation()了,那么它要用的时候,确定要getAnimation(),找到这个方法在哪里调用就行了。因而经过搜索,在View.draw(Canvas, ViewGroup, long)方法中发现了它的调用,代码片断以下:
其中调用了View.drawAnimation()方法。
代码片断以下:
其中调用了Animation.getTransformation()方法。
该方法直接调用了两个参数Animation.getTransformation()方法。
该方法先将参数currentTime处理成一个float表示当前动画进度,好比说,一个2000ms的动画,已经执行了1000ms了,那么进度就是0.5或者说50%。
而后将进度值传入插值器(Interpolator)获得新的进度值,前者是均匀的,随着时间是一个直线的线性关系,而经过插值器计算后获得的是一个曲线的关系。
而后将新的进度值和Transformation对象传入applyTranformation()方法中。
Animation的applyTransformation()方法是空实现,具体实现它的是Animation的四个子类,而该方法正是真正的处理动画变化的过程。分别看下四个子类的applyTransformation()的实现。
ScaleAnimation
AlphaAnimation
RotateAnimation
TranslateAnimation
可见applyTransformation()方法就是动画具体的实现,系统会以一个比较高的频率来调用这个方法,通常状况下60FPS,是一个很是流畅的画面了,也就是16ms,为了验证这一点,我在applyTransformation方法中加入计算时间间隔并打印的代码进行验证,代码以下:
最终获得的log以下图所示:
右侧是"手动"计算出来的时间差,有必定的波动,但大体上是16-17ms的样子,左侧是日志打印的时间,时间很是规则的相差20ms。
因而,根据以上的结果,能够得出如下内容:
1.首先证实了一点,Animation.applyTransformation()方法,是动画具体的调用方法,咱们能够覆写这个方法,快速的制做本身的动画。
2.另外一点,为何是16ms左右调用这个方法呢?是谁来控制这个频率的?
对于以上的疑问,我有两个猜想:
1.系统本身以postDelayed(this, 16)的形式调用的这个方法。
2.系统一个死循环疯狂的调用,运行一系列方法走到这个位置的间隔恰好是16ms左右,若是主线程卡了,这个间隔就变长了。
在4.1以前是使用handler,4.1以后使用的是VSYNC