Android渲染机制 1. View发起绘制请求 在 View 涉及到 UI 更新的方法中,几乎都会在最后调用 View#invalidate()
或者 View#requestLayout()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class View { public void invalidate () { invalidate(true ); } public void invalidate (boolean invalidateCache) { invalidateInternal(0 , 0 , mRight - mLeft, mBottom - mTop, invalidateCache, true ); } void invalidateInternal (int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { if (...) { mPrivateFlags |= PFLAG_DIRTY; final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); p.invalidateChild(this , damage); } } } public void requestLayout () { mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } } } public abstract class ViewGroup extends View implements ViewParent , ViewManager { public final void invalidateChild (View child, final Rect dirty) { ViewParent parent = this ; if (attachInfo != null ) { do { parent = parent.invalidateChildInParent(location, dirty); } while (parent != null ); } } public ViewParent invalidateChildInParent (final int [] location, final Rect dirty) { ...... if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0 ) { return mParent; } return null ; } }
invalidate()
会逐级递归调用 ViewParent#invalidateChildInParent(...)
;
requestLayout()
会逐级递归调用 mParent.requestLayout()
;
而一个 ViewTree 中,最顶层的 ViewParent 就是 ViewRootImpl,所以实际上 View#invalidate()
和 View#requestLayout()
最终都会调用到 ViewRootImpl#invalidateChildInParent(...)
和 ViewRootImpl#requestLayout()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public final class ViewRootImpl implements ViewParent { final ViewRootHandler mHandler = new ViewRootHandler(); @Override public ViewParent invalidateChildInParent (int [] location, Rect dirty) { checkThread(); if (dirty == null ) { invalidate(); return null ; } ...... invalidateRectOnScreen(dirty); } private void invalidateRectOnScreen (Rect dirty) { localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); ...... scheduleTraversals(); } @Override public void requestLayout () { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true ; scheduleTraversals(); } } }
可以看到,不论是 invalidate()
还是 requestLayout()
,最终在 ViewRootImpl 中都会通过 checkThread()
检查线程,并调用 ViewRootImpl#scheduleTraversals()
发起绘制。但是:
调用 View#invalidate()
会将 View 的 Rect 标记 PFLAG_DIRTY
,然后在下一帧刷新 ViewTree 时,ViewRootImpl 会调用最顶层的 View#draw(Canvas)
,然后逐层向下分发,但只会在具有 PFLAG_DIRTY
时回调 onDraw(Canvas)
。
调用 View#requestLayout()
会将当前 View 标记 PFLAG_FORCE_LAYOUT
,然后在下一帧刷新 ViewTree 时,ViewRootImpl 会调用最顶层的 View#measure()
和 View#layout()
,然后逐层向下分发,但只会在具有 PFLAG_FORCE_LAYOUT
时回调 onMeasure()
和 onLayout()
。
此外,ViewRootImpl 在构造方法中记录了自己的初始化线程,作为允许更新 UI 的线程,并且持有了一个 Handler 成员变量,同样也处于 ViewRootImpl 的初始化线程。View#postInvalidate()
和 View#postInvalidateDelayed(delayTime)
会向这个 Handler 发送一个事件,携带了调用的 View 本身,然后 ViewRootImpl 会在事件回调中直接调用这个 View#invalidate()
,因此相当于把 View#invalidate()
抛到 ViewRootImpl 初始线程中执行,多用于跨线程请求刷新 UI。
2. Android绘制渲染流程 上文提到,View 在更新 UI 时会通过 invalidate()
或者 requestLayout()
请求 ViewRootImpl 绘制,最终都会调用到 ViewRootImpl#scheduleTraversals()
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public final class ViewRootImpl implements ViewParent { Choreographer mChoreographer; final ViewRootHandler mHandler = new ViewRootHandler(); final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); final class TraversalRunnable implements Runnable { @Override public void run () { doTraversal(); } } void doTraversal () { if (mTraversalScheduled) { mTraversalScheduled = false ; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); performTraversals(); } } public ViewRootImpl (Context context, Display display) { mChoreographer = Choreographer.getInstance(); } void scheduleTraversals () { if (!mTraversalScheduled) { mTraversalScheduled = true ; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null ); ...... } } }
可以看到,scheduleTraversals()
先发送一条屏障消息阻塞了 ViewRootImpl 线程,然后 post 了一个 Runnable,这个 Runable 的目的是在回调时调用 performTraversals()
,这也是绘制真正开始的入口。因此重点就在于 mChoreographer.postCallback(...)
内的逻辑。
2.1 注册绘制信号回调 Choreographer 中一共定义了 5 种帧同步信号回调类型,在 ViewRootImpl#scheduleTraversals()
中调用的 mChoreographer#postCallback(...)
指定了回调类型为 CALLBACK_TRAVERSAL
,表示 绘制信号回调 ,并注册了一个回调 Runnable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public final class Choreographer { public static final int CALLBACK_INPUT = 0 ; public static final int CALLBACK_ANIMATION = 1 ; public static final int CALLBACK_INSETS_ANIMATION = 2 ; public static final int CALLBACK_TRAVERSAL = 3 ; public static final int CALLBACK_COMMIT = 4 ; private static final int CALLBACK_LAST = CALLBACK_COMMIT; private final CallbackQueue[] mCallbackQueues; private Choreographer (Looper looper, int vsyncSource) { mLooper = looper; mHandler = new FrameHandler(looper); mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1 ]; for (int i = 0 ; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } } public void postCallback (int callbackType, Runnable action, Object token) { postCallbackDelayed(callbackType, action, token, 0 ); } public void postCallbackDelayed (int callbackType, Runnable action, Object token, long delayMillis) { ...... postCallbackDelayedInternal(callbackType, action, token, delayMillis); } private void postCallbackDelayedInternal (int callbackType, Object 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); } } } private final class FrameHandler extends Handler { @Override public void handleMessage (Message msg) { switch (msg.what) { case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); break ; } } } void doScheduleCallback (int callbackType) { synchronized (mLock) { if (!mFrameScheduled) { final long now = SystemClock.uptimeMillis(); if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) { scheduleFrameLocked(now); } } } } }
Choreographer#postCallback(...)
主要是为了注册一个帧同步信号的回调,包含了两个步骤:
先根据传入的 callbackType 向对应类型的 CallbackQueue 中添加一个 Callback;
然后根据是否延时判断是直接调用 Choreographer#scheduleFrameLocked(now)
或是延时后调用。
2.2 发起帧同步信号 上文提到 Choreographer 一共有 5 种类型的帧同步信号回调,每一种类型的回调都是通过 CallbackQueue 链表存储的,Choreographer#postCallback(...)
就是为了注册回调:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public final class Choreographer { private void postCallbackDelayedInternal (int callbackType, Object action, Object token, long delayMillis) { ...... mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); ...... scheduleFrameLocked(now); } }
Choreographer 注册绘制信号回调分为两步:
创建一个 CallbackRecord 对象用于存储 ViewRootImpl 注册的回调,然后把新创建的 CallbackRecord 按照时间戳的顺序添加到对应的 CALLBACK_TRAVERSAL
类型的 CallbackQueue 中。
向 FrameDisplayEventReceiver 发起一个帧同步信号的请求,并等待帧同步信号回调,然后再按顺序依次回调 5 种 CallbackQueue 中的所有回调。
(1)创建 CallbackRecord 并插入 CallbackQueue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public final class Choreographer { private final class CallbackQueue { private CallbackRecord mHead; public void addCallbackLocked (long dueTime, Object action, Object token) { CallbackRecord callback = obtainCallbackLocked(dueTime, action, token); CallbackRecord entry = mHead; if (entry == null ) { mHead = callback; return ; } if (dueTime < entry.dueTime) { callback.next = entry; mHead = callback; return ; } while (entry.next != null ) { if (dueTime < entry.next.dueTime) { callback.next = entry.next; break ; } entry = entry.next; } entry.next = callback; } } }
(2)向 FrameDisplayEventReceiver 发起帧同步信号:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public final class Choreographer { private final FrameDisplayEventReceiver mDisplayEventReceiver; private void scheduleFrameLocked (long now) { if (!mFrameScheduled) { mFrameScheduled = true ; if (USE_VSYNC) { if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true ); mHandler.sendMessageAtFrontOfQueue(msg); } } else { Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true ); mHandler.sendMessageAtTime(msg, nextFrameTime); } } } private void scheduleVsyncLocked () { mDisplayEventReceiver.scheduleVsync(); } private final class FrameHandler extends Handler { switch (msg.what) { case MSG_DO_FRAME: doFrame(System.nanoTime(), 0 ); break ; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break ; } } void doScheduleVsync () { synchronized (mLock) { if (mFrameScheduled) { scheduleVsyncLocked(); } } } }
Choreographer 发起帧同步信号主要包括注册回调和发起信号两步:
创建一个 CallbackRecord 用于存储回调,然后根据 Choreographer#postCallback(...)
传入的 callbackType,将创建的 CallbackRecord 按 dueTime 时间顺序插入到对应类型的 CallbackQueue 中。
发起帧同步信号,并等待回调,根据 Android 版本分为两种方式:
Android 4.1 之前不使用 VSync 信号,则直接发送一条异步消息,调用 Choreographer#doFrame()
。
Android 4.1 之后默认使用 VSync 信号,则会调用 FrameDisplayEventReceiver#scheduleVsync()
2.3 等待帧同步信号 上文提到,Android 4.1 之前和之后,发起帧同步信号请求的方式不同:
Android 4.1 之前直接调用了 doFrame(...)
,内部根据当前时间、上一帧时间、帧率时间间隔判断是否需要回调。
Android 4.1 之后则是通过发起下一个 VSync 信号监听的方式,调用 FrameDisplayEventReceiver#scheduleVsync()
,由 Native 层通知下一帧 VSync 信号后再回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public final class Choreographer { private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { @Override public void onVsync (long timestampNanos, long physicalDisplayId, int 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); } } } public abstract class DisplayEventReceiver { public void scheduleVsync () { ...... nativeScheduleVsync(mReceiverPtr); } }
实际上,是否使用 VSync 信号的区别主要在于下一帧帧同步信号的接收方式:
通过 doFrame(...)
中根据上一帧时间、当前时间、帧率间隔时间计算。
接收来自 SurfaceFlinger 的 VSync 信号。
这个区别只是体现在 Choreographer 如何知道需要下一帧帧同步,对于收到信号后回调到 ViewRootImpl,则最终都是通过 doFrame(...)
回调的。
2.4 回调帧同步信号 根据上文的分析,Choreographer 在接收到帧同步信号后,最终都会通过 doFrame(...)
回调给 ViewRootImpl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public final class Choreographer { void doFrame (long frameTimeNanos, int frame) { ...... try { mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } } void doCallbacks (int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null ) { return ; } ...... for (CallbackRecord c = callbacks; c != null ; c = c.next) { c.run(frameTimeNanos); } } } public void postFrameCallback (FrameCallback callback) { postFrameCallbackDelayed(callback, 0 ); } public void postFrameCallbackDelayed (FrameCallback callback, long delayMillis) { postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN, delayMillis); } private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; public Object token; public void run (long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); } } } }
ViewRootImpl 在 scheduleTraversals()
中调用了:
1 2 3 4 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null );
所以在回调这个 CallbackRecord#run()
时 token == null
,就会执行 ((Runnable)action).run()
的逻辑,也就是执行 ViewRootImpl#mTraversalRunnable#run()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public final class ViewRootImpl implements ViewParent { final class TraversalRunnable implements Runnable { @Override public void run () { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); void doTraversal () { if (mTraversalScheduled) { mTraversalScheduled = false ; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); performTraversals(); } } }
此时回调到 ViewRootImpl 之后,就会移除一开始在 scheduleTraversals()
中发送的屏障消息,然后调用 performTraversals()
正式开始绘制流程。
需要注意的是,移除屏障消息后,由于接下来的 performTraversals()
仍然在同一个线程的调用栈中,因此 ViewRootImpl 初始线程中原来被阻塞的同步消息,是在 performTraversals()
之后才执行的。
通过源码可知,Choreographer.getInstance().postFrameCallback(FrameCallback)
可以向 Choreographer 注册一个 Animation 类型的 VSync 回调,并在每一次收到来自 SurfaceFlinger 的 VSync 信号时回调。回调方法 doFrame(long frameTimeNanos)
携带的 frameTimeNanos
表示 Choreographer 收到本次 VSync 信号时的时间戳,因此可以通过比较两次 VSync 信号时间等方式获取帧率、绘制耗时等信息。
当 ViewRootImpl 收到了帧同步信号的渲染回调后,就会调用 performTraversals()
开始绘制 ViewTree:
1 2 3 4 5 6 7 8 9 10 11 12 public final class ViewRootImpl implements ViewParent { private void performTraversals () { ...... host.dispatchAttachedToWindow(mAttachInfo, 0 ); ...... performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ...... performLayout(lp, mWidth, mHeight); ...... performDraw(); } }
ViewRootImpl 会分别调用其顶层 View 的 measure(...)
、layout(...)
、draw(...)
方法,并逐层向下分发,每一层 View 都会判断:
如果自己具有 PFLAG_FORCE_LAYOUT
标志,则 measure(...)
会回调 onMeasure(...)
,layout(...)
会回调 onLayout(...)
。
如果自己具有 PFLAG_DIRTY
标志,则 draw(...)
会回调 onDraw(...)
。
在 Measure 阶段,getMeasureWidth()
是 View 测量自己的宽度,getWidth()
是 View 的父布局测量 View 的宽度。当 View 需要的宽度大于屏幕宽度时,getMeasureWidth()
得到的仍然是 View 需要的宽高,而 getWidth()
得到的是屏幕实际显示的宽度。
其中,performDraw()
根据硬件绘制、软件绘制分为两种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public final class ViewRootImpl implements ViewParent { private void performTraversals () { ...... performDraw(); } private void performDraw () { ...... boolean canUseAsync = draw(fullRedrawNeeded); } private boolean draw (boolean fullRedrawNeeded) { if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this ); } else { if (!drawSoftware(...)) { return false ; } } } private boolean drawSoftware (Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) { final Canvas canvas; ...... canvas = mSurface.lockCanvas(dirty); ...... surface.unlockCanvasAndPost(canvas); } } public final class ThreadedRenderer extends HardwareRenderer { void draw (View view, AttachInfo attachInfo, DrawCallbacks callbacks) { updateRootDisplayList(view, callbacks); ...... } private void updateRootDisplayList (View view, DrawCallbacks callbacks) { updateViewTreeDisplayList(view); ...... } private void updateViewTreeDisplayList (View view) { ...... view.updateDisplayListIfDirty(); } } public class View { public RenderNode updateDisplayListIfDirty () { ...... final RecordingCanvas canvas = renderNode.beginRecording(width, height); ...... draw(canvas); ...... renderNode.endRecording(); } }
ViewTree 依次绘制自己的试图,完成整个绘制流程。
2.6 View准确测量宽高 当一个 View 被加载时如果想要正确获取 View 的宽高,通常会通过 View#post(Runnable)
的方式,在回调中获取,而这种方式之所以有效,正是因为 ViewRootImpl#scheduleTraversals()
中的屏障消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class View { public boolean post (Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null ) { return attachInfo.mHandler.post(action); } getRunQueue().post(action); return true ; } private HandlerActionQueue getRunQueue () { if (mRunQueue == null ) { mRunQueue = new HandlerActionQueue(); } return mRunQueue; } void dispatchAttachedToWindow (AttachInfo info, int visibility) { if (mRunQueue != null ) { mRunQueue.executeActions(info.mHandler); mRunQueue = null ; } } }
调用 View#post(Runnable)
时会先判断 View 是否已经具有 AttachInfo:
如果有则说明该 View 已经被关联到 Window 上了,则直接通过 AttachInfo 获取 ViewRootImpl 的 Handler 并向 ViewRootImpl 线程发送一条同步消息,由该消息回调 post 的 Runnable。
否则说明 View 还没有被关联到 Window 上,也可以理解为 Window 还未创建完毕,则把 Runnable 添加到 View 持有的一个队列中,等待 View 被关联到 Window 之后(对应 ViewRootImpl 逐层分发 dispatchAttachedToWindow(...)
),再一次性把队列中所有的 Runnable 都已同步消息发送到 ViewRootImpl 线程中。
View#post(Runnable)
通过这种方式,确保了 Runnable 始终在 Window 已经创建并与 View 关联后,才在 ViewRootImpl 线程中被回调。
当 Window 被创建初始化时,WindowManager#addView(...)
中调用了 ViewRootImpl#setView(...)
,其内部调用了 ViewRootImpl#requestLayout()
,并触发 ViewRootImpl#scheduleTraversals()
。
当 Window 已经创建完毕并且已经与 View 关联后,每一次 View 发生 UI 的改变,都会调用 invalidate()
或 requestLayout()
,并且最终都会触发 ViewRootImpl#scheduleTraversals()
。
综上所述,不论 Window 处于哪种状态,只要 UI 有变更,View#post(Runnable)
的 Runnable 都会被 scheduleTraversals()
中发送的屏障消息阻塞,并在 ViewRootImpl 收到下一帧 VSync 信号、调用 performTraversals()
完成下一帧的绘制之后,才移除屏障消息并依次执行这些同步消息中的 Runnable,所以这些 Runnable 执行的时候,一定能保证当前帧已经绘制完毕,绘制完毕意味着 View 已经被 Measure,所以获取的宽高是正确的。
不过,View#post(Runnable)
在 Window 初次创建时的流程与 UI 发生变更时的流程有略微差别。
当 Window 是已经创建完毕并与 View 关联后,则 View 已经具有 AttachInfo,因此 post 的 Runnable 会直接发送到 ViewRootImpl 线程的同步消息中,如果 UI 有变更,则会被屏障消息阻塞,等到下一帧绘制完毕后回调。需要注意的是,同步消息被阻塞并不意味着线程被阻塞,即使同步消息被阻塞,依旧可以继续发送消息,只是 Looper 不会取出消息并回调。
当 Window 被首次创建和初始化,此时还未与 View 关联。在 ViewRootImpl#setView(...)
阶段,会调用一次 requestLayout()
,并绘制第一帧,但此时 View 尚不具有 AttachInfo,Runnable 仍被存在 RunQueue 中。然后在 performTraversals()
绘制完首帧后,由于 mFirst == true
才会调用 host.dispatchAttachedToWindow(mAttachInfo, 0)
将 AttachInfo 分发给各个 View,此时 View 才将 RunQueue 中的 Runnable 再次发送到 ViewRootImpl 线程,因此需要在第二帧绘制后,才能回调 Runnable。
2.7 Activity首帧监控 通过上述分析结合 Activity 启动流程,即可知道 Activitiy 首帧的渲染时机以及对应的监控方式:
onCreate()
中使用 View#post(Runnable)
,但回调时只代表调用的这个 View 开始绘制,并不是首帧渲染完成。
改进方案:给 DecorView 添加一个 1 像素的 View,使得该 View 称为 DecorView 中最后一个子 View,然后在该 View 的 onDraw()
中通过 Choreographer.getInstance().postFrameCallback(Runnable)
监听下一个 VSync 信号,因为 VSync 只在需要绘制的时候才会触发,绘制一帧回调一次,所以在 onDraw()
里面确保已经绘制完成,然后下一次 VSync 信号回调的时候表示即将开始下一次绘制,此时的时间就能近似认为是首帧绘制完成的时间。
给 DecorView 添加 addOnDrawListener(OnDrawListener)
,DecorView 开始绘制时会回调,但是此时 DecorView 内的所有子 View 都还没有开始绘制,并不能代表整个 Activity 绘制完成,只是在 View 很少的时候时间比较接近。
改进方案:在 DecorView 的 OnDrawListener 回调中重新 post 一个新的回调,则会在当前帧绘制完成后、下一帧即将开始绘制之前回调。
3. Surface机制 Android 中的渲染和绘制,最终是通过 Surface 承载的,Surface 是一个真正用于渲染像素的区域,每个 Window 都有自己的 Surface,保存在 Window 的 ViewRootImpl 中,一个 App 可以有多个 Window,因此可以拥有多个 Surface,由 SurfaceFlinger 承担统一管理以及合成这些 Surface 的职责。
ViewRootImpl 在 performTraversals()
绘制流程的 draw()
阶段,通过 surface.lock()
获取当前 Window 的 Surface 的 Canvas,然后把 Canvas 分发到每个 View,每个 View 自己绘制完之后再分发到自己的子 View,所以实际上一个 Window 内 ViewTree 中所有 View 都会共同渲染到所在 Window 的 Surface 上。
Window、Surface、SurfaceFlinger 的关系大致如下图所示:
当 App 启动并创建一个 Window 后,ViewRootImpl 向 WMS 通知该 Window 的 Surface,WMS 判断 SurfaceFlinger 中是否已经存有该 Window 对应的 Layer,如果没有则在 SurfaceFlinger 中注册。注册的时候首先会创建一个与 App 一一对应的 Binder 代理对象 Client
,用于 App 与 SurfaceFlinger 通信。
当 App 需要渲染的时候,就会通过 Client 请求 SurfaceFlinger,把需要绘制的区域、位置等元数据发送给 SurfaceFlinger。由于 App 可能存在很多个 Surface,所以为了增强数据通信的性能,SurfaceFlinger 为每一个 App 创建了一块共享内存 SurfaceClient,App 直接把需要渲染的 UI 元数据直接写入 SurfaceClient,SurfaceFlinger 直接从 SurfaceClient 读取,避免了内核多次拷贝数据。
每个 App 在 SurfaceFlinger 中都有自己对应的 SurfaceClient 共享内存区域,而 SerfaceClient 在每个 App 对应的 SurfaceClient 中各维护了一个 SharedBufferStack 的数组,长度为 31。
每一个 SharedBufferStack 对应一个实际的 Surface,SharedBufferStack 的意义就是用作渲染缓冲区。常见的 UI 刷新模型采用的是双缓冲,一个 BackBuffer 一个 FrontBuffer,显示屏在读取渲染 FrontBuffer 时,UI 元素可以同时向 BackBuffer 写入下一部分需要渲染的数据,然后在 BackBuffer 写入完成后,再交换两个缓冲区,使得原来的 BackBuffer 变为 FrontBuffer,显示屏读取新的 FrontBuffer 渲染显示。而 SharedBufferStack 就是扩展了这个缓冲模型,使得一个 Surface 可以具有 2 个以上的缓冲区,Android 2.3 中 SharedBufferStack 有 2 个缓冲区,Android 4.1 中 SharedBufferStack 有 3 个缓冲区。
一个 SharedBufferStack 中的结构包括:
多个缓冲区 Buffer:每一个 Buffer 保存的就是需要渲染的 UI 的元数据,但这些元数据并不包含具体需要渲染的 UI 数据,而是通过指向一个 GaphicBuffer,由 GaphicBuffer 保存了所有实际渲染的 UI 数据,当 SurfaceFlinger 真正绘制渲染的时候,就会通过 Buffer 找到指向的 GaphicBuffer,从 GaphicBuffer 中取出真实的 UI 数据来渲染。GraphicBuffer 中包含了一个 buffer_handle_t 对象用于描述实际渲染数据的缓冲区 ,而这个缓冲区是在 HAL 层的 Gralloc 模块在系统帧缓冲区 FrameBuffer 或者匿名共享内存 ASHMEM (Anonymous Shared Memory) 中分配的。
两个计数器:
available 表示有几个可供写入的空闲缓冲区。
queued 表示有几个已经写入等待被读取渲染的缓冲区。
三个指针:
head 和 tail 标记了可供写入的空闲 Buffer 的范围。
queue_head 标记了已准备好的可供读取渲染的 Buffer 的头部。
Linux 中所有的设备都通过 主设备名 和 从设备名 来唯一定位。Linux 在启动时会创建一个类别为 graphics
、名叫 fb0
的设备,用来描述系统中的第一个 帧缓冲区 ,也就是第一个显示屏。其中 fb (Frame Buffer)
是主设备名,0
是从设备名,系统必须至少具有一个显示屏,所以 fb0
设备必定存在,Android 基于 Linux 从内核上是支持多显示屏的,但是 Android 在实现上只使用一个显示屏。
因此由于 buffer_handle_t 在 FrameBuffer 中分配,当 SurfaceFlinger 把实际的渲染数据写入 buffer_handle_t 后,也就相当于渲染到屏幕上。
4. SurfaceView原理 SurfaceView 与 普通 View 不同的地方在于:
一个 Window 下只有一个 ViewRootImpl,ViewRootImpl 持有 DecorView,DecorView 作为 ViewTree 的顶层 View,整个 ViewTree 中只有 ViewRootImpl 中持有一个 Surface 对象。
而 SurfaceView 拥有一个自己的 Surface。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public final class ViewRootImpl implements ViewParent { private void performTraversals () { if (mFirst) { host.dispatchAttachedToWindow(mAttachInfo, 0 ); } if (viewVisibilityChanged) { host.dispatchWindowVisibilityChanged(viewVisibility); } performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); performLayout(lp, mWidth, mHeight); ...... host.gatherTransparentRegion(mTransparentRegion); mWindowSession.setTransparentRegion(mWindow, mTransparentRegion); ...... performDraw(); } @Override public void requestTransparentRegion (View child) { checkThread(); if (mView == child) { mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS; requestLayout(); } } } public class View { void dispatchAttachedToWindow (AttachInfo info, int visibility) { onAttachedToWindow(); } public void dispatchWindowVisibilityChanged (@Visibility int visibility) { onWindowVisibilityChanged(visibility); } } public abstract class ViewGroup extends View implements ViewParent , ViewManager { @Override public void requestTransparentRegion (View child) { if (child != null ) { child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS; if (mParent != null ) { mParent.requestTransparentRegion(this ); } } } }
SurfaceView 重写了 View 中的部分方法,使其可以单独控制自己的 Surface 以及可见区域:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class SurfaceView extends View { IWindowSession mSession; final Surface mSurface = new Surface(); MyWindow mWindow; @Override protected void onAttachedToWindow () { super .onAttachedToWindow(); mParent.requestTransparentRegion(this ); mSession = getWindowSession(); } @Override protected void onWindowVisibilityChanged (int visibility) { super .onWindowVisibilityChanged(visibility); ...... updateWindow(false , false ); } private void updateWindow (boolean force, boolean redrawNeeded) { ...... mSurfaceLock.lock(); try { final int relayoutResult = mSession.relayout( mWindow, mLayout, mWidth, mHeight, visible ? VISIBLE : GONE, false , mWinFrame, mContentInsets, mVisibleInsets, mConfiguration, mSurface); } finally { mSurfaceLock.unlock(); } } }
SurfaceView 在 SurfaceFlinger 中的 Layer 的 Z-Order 位于所属 Window 的 Layer 的下方,所以 SurfaceView 才需要在 onAttachedToWindow() 中调用 mParent.requestTransparentRegion(this) 让 Window 对应 Surface 的这些区域变为透明,否则会被遮盖。但是这个请求设置为透明,设置的是 Window 对应 Surface 的区域,而不是 ViewGroup 或 ViewGroup 包含的 View,它们是否透明依然是由 onDraw(Canvas)
决定的,mParent.requestTransparentRegion(this)
仅仅只是为了把 SurfaceView 所在的 Rect 一层层传递到 ViewRootImpl,并由 ViewRootImpl 收集汇总而已。
参考文献 View 的绘制流程:
Android Surface 机制: