本文主要講 view.requestLayout() 到 view 重新繪制成功流程恢着。
view.requestLayout 調(diào)用的是 parent.requestLayout,直到 DecorView 最終到 ViewRootImpl.requestLayout 方法袜刷。
提示: requestLayout() 跟 invalidate() 區(qū)別在于 PFLAG_FORCE_LAYOUT鸦难、PFLAG_INVALIDATED侨嘀,invalidate 不會重新測量布局镰惦,只會重新繪制
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//1. 當(dāng)前調(diào)用線程檢查
checkThread();
mLayoutRequested = true;
//2.
scheduleTraversals();
}
}
void checkThread() {
//1.1 調(diào)用線程必須在主線程
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//首先中執(zhí)行同步屏障,msg.target 為空,該 msg 會插入到 messageQueue 合適的位置,
//當(dāng) messageQueue.next() 發(fā)現(xiàn)有 target 為空的空的消息侥钳,則優(yōu)先找 msg.isAsynchronous() 的消息插隊,此處是為了 vsync 消息優(yōu)先執(zhí)行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//2.1 主要將 mTraversalRunnable 傳遞給 mChoreographer
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
}
Choreographer
調(diào)用棧:mChoreographer.postCallback(int callbackType, Runnable action, Object token) --> postCallbackDelayed() --> postCallbackDelayedInternal()
//參數(shù) action 是 ViewRootImpl 中 mTraversalRunnable
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
...
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
//2.1.1 Choreographer 將任務(wù)保存在 mCallbackQueues 中柄错,callbackType 為 CALLBACK_TRAVERSAL舷夺,等待 vsync 到來時通過類型回調(diào) runnable
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
//如果不是延時消息,立即請求 vsync
if (dueTime <= now) {
//2.1.2
scheduleFrameLocked(now);
} else {
////2.1.3 延時消息售貌,發(fā)送 MSG_DO_SCHEDULE_CALLBACK 的同步消息給 FrameHandler给猾,
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
//設(shè)置為異步消息,結(jié)合第 2 步的 postSyncBarrier 使用颂跨,是的該消息優(yōu)先普通消息執(zhí)行
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
...
// 2.1.2.1 當(dāng)運行在 Looper 線程敢伸,則立刻調(diào)度 vsync,否則發(fā)送消息到 UI 線程再調(diào)度 vsync
if (isRunningOnLooperThreadLocked()) {
//下面再看該方法
scheduleVsyncLocked();
} else {
//// 2.1.2.2 發(fā)送消息到 UI 線程恒削,請求 vsync
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
// 否則執(zhí)行 doFrame
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
@UnsupportedAppUsage
private void scheduleVsyncLocked() {
//通過 FrameDisplayEventReceiver 調(diào)度 vysnc池颈,最終調(diào)用 nativeScheduleVsync() 方法
mDisplayEventReceiver.scheduleVsync();
}
至此從調(diào)用 requestLayout 到請求 Vsync 信號過程已經(jīng)結(jié)束。
下面看收到 Vsync 信號后钓丰,如何處理 mTraversalRunnable 任務(wù)躯砰。
FrameDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
...
//收到 vsync 信號回調(diào)該方法
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
...
//3.1 FrameDisplayEventReceiver 是一個 Runnable 的 callback,把此對象作為異步消息發(fā)送給 FrameHandler携丁,隨后 handler 會調(diào)用改對象的 run 方法琢歇。
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;
//3.2 FrameHandler 執(zhí)行該方法,回到 Choreographer 的 doFrame 方法
doFrame(mTimestampNanos, mFrame);
}
}
回到 Choreographer
@UnsupportedAppUsage
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
...
//省略梦鉴,主要一計算掉幀邏輯李茫,二是記錄幀繪制信息
...
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
//處理多種callback
//依次是 input 調(diào)用棧,會回調(diào)到 DecorView 的 dispatchTouchEvent
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
//二是 animation 用棧,
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
////三是Traversal調(diào)用棧,即最發(fā)送給 Choreographer 的任務(wù) mTraversalRunnable
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
...
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
//通過 callbackType 取出 callbacks
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
...
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
//調(diào)用 Runnable.run 方法尚揣,mTraversalRunnable run 會調(diào)用 doTraversal() 方法
c.run(frameTimeNanos);
}
} finally {
...
}
}
doTraversal()方法則是 測量涌矢、布局、繪制 入口快骗,此處不做分析娜庇。
Vsync 垂直同步:
????涉及到垂直刷新脈沖、vsync 方篮、gpu 緩沖區(qū) Frame Buffer名秀、Back Buffer 三重緩存,跟 Choreographer
????gpu 像素柵格化
????垂直同步使得顯卡的輸出幀數(shù)和屏幕的刷新速度保持一致藕溅,其中 vsync 用來同步信息匕得,buffer 緩存數(shù)據(jù),當(dāng) vsync 出現(xiàn)時,cpu 會立即處理下一幀數(shù)據(jù)寫入到緩存中汁掠,
????之后gpu再渲染數(shù)據(jù)寫在同一個緩存中略吨,當(dāng)vsync時,下一幀的 buffer 跟當(dāng)前幀所在的buffer數(shù)據(jù)交換考阱,當(dāng)如果之前幀未顯示完翠忠,是不會進行數(shù)據(jù)交換的。屏幕掃描下一次的數(shù)據(jù)顯示乞榨。
????當(dāng)一個信號來時秽之,假設(shè)a b buffer都被占用,此時gpu使用c緩存下一幀的數(shù)據(jù)吃既,可以有效減少掉幀的幾率考榨。
Choreographer(編舞者)
FrameHandler 處理 3 各消息類型
MSG_DO_FRAME:開始渲染下一幀操作
MSG_DO_SCHEDULE_VSYNC:請求 Vsync 信號
MSG_DO_SCHEDULE_CALLBACK:請求執(zhí)行 callback
FrameDisplayEventReceiver 用來接收垂直跟發(fā)送同步信號
總結(jié):
1、view.requestLayout 調(diào)用的是 parent.requestLayout ,直到 DecorView 最終到 ViewRootImpl.requestLayout 方法鹦倚。
2河质、首先判斷正在測量布局,沒有則 checkThread 檢驗當(dāng)前是否在主線程申鱼。在 scheduleTraversals 首先中執(zhí)行同步屏障愤诱,其次再將任務(wù) postCallback 給 Choreographer,Choreographer 將任務(wù)保存在 mCallbackQueues 中捐友,同時發(fā)送 MSG_DO_SCHEDULE_CALLBACK 的同步消息給FrameHandler淫半。FrameHandler 的優(yōu)先執(zhí)行 CALLBACK 同步消息調(diào)用 doScheduleCallback,mCallbackQueues 不為空且 callback 不是延遲執(zhí)行匣砖,調(diào)用 scheduleFrameLocked 方法請求 Vsync 信號科吭。當(dāng)運行在 Looper 線程,則立刻調(diào)度 vsync猴鲫,否則对人,發(fā)送消息到UI線程再調(diào)度 vsync。其中是通過 FrameDisplayEventReceiver 調(diào)度 vysnc拂共。
FrameDisplayEventReceiver 有兩個作用牺弄,一個是 scheduleVsync 請求調(diào)度,另一個是接收 vsync 信號回調(diào) onVsync宜狐,當(dāng)接收到 vsync 信號時势告,調(diào)用doFrame 方法,開始渲染下一幀抚恒。
doFrame 可以分為三步:一是計算掉幀邏輯咱台,二是記錄幀繪制信息,三是處理多種 callback俭驮,依次是 input 調(diào)用棧,會回調(diào)到 DecorView 的 dispatchTouchEvent回溺。
二是 animation 調(diào)用棧,執(zhí)行動畫;三是 Traversal 調(diào)用棧遗遵,即最發(fā)送給 Choreographer 的任務(wù)
doTraversal()->performTraversals() ->
performMeasure():measere():onMeasure()->
performLayout():layout():onLayout()->
performDraw():drawSoftware():draw()->drawBackground()->保存當(dāng)前圖層信息(可跳過) ->onDraw()-> dispatchDraw()->繪制View邊緣萍恕、陰影效果(可跳過)->onDrawForeground()裝飾、滾動條.
動畫如何流暢執(zhí)行:調(diào)用animation.start時瓮恭,最終在AnimationHandler會給Choreographer.FrameCallback 回調(diào) doFrame雄坪,里面 post了自己。