内容
概述
众所周知, 在Android中每⼀个Activity的展示的载体都是PhoneWindow, 包括顶部的状态栏与底 部的导航栏也都是系统的Window。 而Window作为ViewTree的载体, 内部的展示由ViewTree来实 现。所以展示的组织结构是这样的:
由上图可⻅, 在Android中View管理的数据结构是典型的树形结构, ⽽所有树形结构的根节点 就是ViewRoot; 然⽽ViewRoot并不是⼀个View, 它本⾝只是⼀个ViewParent, 负责作为 WindowManager与View交互的中间⻆⾊。
View体系的渲染包括两部分: View变更申请Vsync信号与Vsync接收重新构建渲染树。
-
View变更申请Vsync信号 : 从发⽣变更的叶⼦节点向根节点回溯, 将Travales加⼊到 callback队列, 并请求下⼀次Vsync信号
-
Vsync接收重新构建渲染 : 从ViewRoot开始, 向下遍历ViewTree; 软件渲染则通过 View.draw进⾏重绘, 最后通过
unlockCanvasAndPost
通知SurfaceFlinger
合成; 硬件渲染 通过updateDisplayListIfDirty更新渲染树, 通过syncAndDrawFrame
通知RenderThread
绘制。
requestLayout
View.requestLayout是从发⽣变更的View开始, 先让MeasureCache失效, 再对当前View打上PFLAG_FORCE_LAYOUT
和 PFLAG_INVALIDATED
标识; 然后不断调⽤mParent.requestLayout按 深度标记整个⼦树。
public void requestLayout() {
// note jin: 先让MeasureCache失效, 再对当前View打上
// PFLAG_FORCE_LAYOUT 和 PFLAG_INVALIDATED标识
if (mMeasureCache != null) mMeasureCache.clear();
……
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
// note jin: 然后不断调用 mParent.requestLayout 按深度标记整个子树
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
Invalidate
和requestLayout
相似的, 也是从变更View向上回溯的过程。 区别在于Invalidate
回溯时,会 给当前View打上PFLAG_INVALIDATED
和PFLAG_DIRTY
标签, 并使绘制的缓存⽆效化, 同时会从 View到mParent不断校正脏区的实际位置区间。
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
// note jin: 如果View不可⻅, 则不需要处理
if (skipInvalidate()) {
return;
}
……
// note jin: 添加DIRTY标识
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
// note jin: 添加INVALIDATE标识, 并去掉CACHE有效标识
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
//// note jin: 将损坏矩形传播到父视图。
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);
// note jin:
p.invalidateChild(this, damage);
}
……
}
}
ViewRoot.requestLayout && invalidateRectOnScreen
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
// Add the new dirty rect to the current one
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
if (!intersected) {
localDirty.setEmpty();
}
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
// note jin:
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// note jin: 发送一个同步屏障,优先执行屏幕刷新
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// note jin: 设置一个回调,发送一个异步的 message,此消息因为屏障会优先执行,刷新屏幕
// note jin: CALLBACK_TRAVERSAL
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
我们发现, ⽆论是requestLayout还是Invalidate, 最终的结果都是scheduleTravels; ⽽ scheduleTravels的实现可以理解为3点:
-
postCallback -> mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token)
, 将回调加⼊到Choregrapher的队列中, 等待Vsync通知执⾏ -
scheduleVsyncLocked
: 申请下⼀帧Vsync信号 -
notifyRendererOfFramePending
: 告诉RenderThread⻢上有⼀帧要来了
performTravels
当在FrameDisplayEventReceiver接收到Vsync信号后, 第⼀步就是通过Choreographer中的 Handler发送事件, 进⽽调⽤doFrame。
而在Choreographer中存储不同类型的Callback, 管理的⽅式是链表结构的队列。
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
@UnsupportedAppUsage
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}
其中action即为真正的执⾏者, 这⾥是⼀个Runnable. 根据下图可以得知, Vsync最终就是执 ⾏了该action,对应ViewRootImpl
中的 TraversalRunnable
, 其run会执行到 ViewRootImpl#doTraversal
draw的执行需要条件:
- Visible
- !dirty.isEmpty()
- FLAG_INVALIDATE标识(硬件渲染根据该标识进⾏判断reCreateDisplayList)
软件渲染
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
……
// note jin: 申请一块 GraficBuffer
canvas = mSurface.lockCanvas(dirty);
……
……
// note jin: 调用 View 的 draw,从DecorView向下遍历更新
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
try {
// note jin: step3: 通过queueBuffer填充到缓冲区
surface.unlockCanvasAndPost(canvas);
} ……
}
return true;
}
硬件渲染
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
choreographer.mFrameInfo.markDrawStart();
// note jin: 硬件更新
updateRootDisplayList(view, callbacks);
……
// note jin: 通知 RenderThread 渲染
int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
……
}