在Android中我們知道,Activity啟動完成之后,走完onResume方法之后胸梆,會進行window的添加蔗衡。添加過程會調(diào)用ViewRootImpl的setView()方法,setView()方法中又會調(diào)用requestLayout()方法來請求繪制布局乳绕,接著會進入到scheduleTraversals()方法,再走到performTraversals()方法逼纸,在performTraversals()方法中洋措,會進行測量、布局杰刽、繪制等三大流程菠发。
除此之外,當(dāng)我們使用ValueAnimator.start()贺嫂、View.invalidate()時滓鸠,最后也會走到ViewRootImpl的scheduleTraversals()方法,想要了解具體的第喳,可以自行查看源碼糜俗,這里就不做過多的說明了。因此曲饱,我們可以得出結(jié)論
所有UI的變化都是走到ViewRootImpl的scheduleTraversals()方法
思考這樣一個問題悠抹?當(dāng)調(diào)用ViewRootImpl的scheduleTraversals()方法之后,會立即就調(diào)用performTraversals()方法嗎扩淀?另外楔敌,我們知道現(xiàn)在Android系統(tǒng)中,當(dāng)我們接收到VSYNC信號之后才會開始繪制驻谆,那VSYNC信號又是在哪里接收的呢卵凑?帶著疑問庆聘,我們展開來說。
首先來看勺卢,ViewRootImpl的scheduleTraversals()方法伙判,代碼如下:
void scheduleTraversals() {
//設(shè)置一個成員變量,標明是否正在執(zhí)行
if (!mTraversalScheduled) {
//標記位設(shè)置為true值漫,避免重復(fù)執(zhí)行
mTraversalScheduled = true;
//添加同步屏障澳腹,目的是屏蔽同步消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
可以看到,上面的代碼中杨何,調(diào)用mChoreographer.postCallback()函數(shù)時候酱塔,post的是一個mTraversalRunnable,我們來看下這個mTraversalRunnable是什么。
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
TraversalRunnable類定義在ViewRootImpl中危虱,如下:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
可以看到羊娃,該類實現(xiàn)了Runnable接口,在run()方法中埃跷,執(zhí)行了doTraversal()函數(shù)蕊玷,我們接著看下doTraversal()函數(shù)。
void doTraversal() {
if (mTraversalScheduled) {
//將在上面scheduleTraversals()方法中設(shè)置的標記為置為false
mTraversalScheduled = false;
//移除同步屏障(使用添加同步屏障的時候返回的mTraversalBarrier變量)
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//真正開始繪制流程
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
綜上分析弥雹,我們可以得出結(jié)論垃帅,在我們調(diào)用ViewRootImpl的scheduleTraversals()方法后,并不是立即就開始執(zhí)行performTraversals()的剪勿,而是需要等到使用Choreographer類的postCallback()方法post出去的Runnable回調(diào)執(zhí)行run()方法的時候贸诚,才開始真正的繪制流程,即調(diào)用performTraversals()方法厕吉。
知道了大致流程酱固,下面我們開始從源碼角度去分析。
添加同步屏障
什么是同步屏障头朱?
在Handler中运悲,消息分為三種:同步消息、異步消息项钮、同步屏障消息班眯,它們?nèi)N消息都是Message,只是屬性不同寄纵。
同步消息和異步消息的區(qū)別在于Message中的flags屬性對應(yīng)的值鳖敷。可以看一下Message的源碼:
/** If set message is asynchronous */
static final int FLAG_ASYNCHRONOUS = 1 << 1;
在Message類中程拭,有一個setAsynchronous()方法:
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
我們可以通過該方法定踱,將Message設(shè)置為異步消息。
除此之外恃鞋,在Handler類中還有一個成員變量mAsynchronous崖媚,該變量在Handler的某一個構(gòu)造函數(shù)中被賦值亦歉,默認是false。
public Handler(@Nullable Callback callback, boolean async) {
......
//這里可以通過Handler的構(gòu)造函數(shù)將mAsynchronous設(shè)置為true
mAsynchronous = async;
}
一旦我們將Hanlder的mAsynchronous設(shè)置為true畅哑,那么在Handler的enqueueMessage方法中肴楷,就會把所有到來的消息設(shè)置為異步消息
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//這里增加了判斷,如果mAsynchronous為true荠呐,設(shè)置Message為異步消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
同步屏障消息
上面我們通過MessageQueue的postSyncBarrier方法添加一個同步屏障赛蔫,具體看下代碼:
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
//注意這里,在創(chuàng)建Message的時候泥张,并沒有給Message設(shè)置target(Handler)屬性
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//MessageQueue中存儲的Message是一個鏈式結(jié)構(gòu)
Message prev = null;
Message p = mMessages;
//根據(jù)Message的時間進行排序呵恢,排序越靠前的消息,when屬性的值越小媚创,越早被執(zhí)行
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
從上述我們可以看到:
1.同步屏障消息和普通的同步異步消息的區(qū)別是渗钉,同步屏障消息沒有target屬性,因為同步屏障消息的作用是屏蔽同步消息從而保證異步消息優(yōu)先執(zhí)行的钞钙。
2.同步屏障消息和普通消息一樣鳄橘,也是根據(jù)時間來插入到消息隊列中的適當(dāng)位置,并且只會擋住它后面的同步消息的分發(fā)芒炼。
3.postSyncBarrier方法返回值是一個int類型瘫怜,該數(shù)值可以用作撤銷同步屏障。
那么同步屏障消息是如何攔住同步消息從而保證異步消息優(yōu)先處理的呢本刽?我們知道宝磨,Handler消息機制中所有的消息的獲取都是從MessageQueue的next方法中,來看下MessageQueue的next方法:
Message next() {
......
for (;;) {
.......
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// target == null條件滿足盅安,說明是同步屏障消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//只取異步消息,即msg.isAsynchronous為true
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
......
}
}
Choreographer的postCallback()分析
方法入?yún)?/h4>
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
Choreographer.CALLBACK_TRAVERSAL
通過源碼可以發(fā)現(xiàn)世囊,第一個參數(shù)在Choreographer類中有五個不同的值
/**
* Callback type: Input callback. Runs first.
* @hide
*/
public static final int CALLBACK_INPUT = 0;
/**
* Callback type: Animation callback. Runs before {@link #CALLBACK_INSETS_ANIMATION}.
* @hide
*/
@TestApi
public static final int CALLBACK_ANIMATION = 1;
/**
* Callback type: Animation callback to handle inset updates. This is separate from
* {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
* {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
* update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
* that contains all the combined updated insets.
* <p>
* Both input and animation may change insets, so we need to run this after these callbacks, but
* before traversals.
* <p>
* Runs before traversals.
* @hide
*/
public static final int CALLBACK_INSETS_ANIMATION = 2;
/**
* Callback type: Traversal callback. Handles layout and draw. Runs
* after all other asynchronous messages have been handled.
* @hide
*/
public static final int CALLBACK_TRAVERSAL = 3;
/**
* Callback type: Commit callback. Handles post-draw operations for the frame.
* Runs after traversal completes. The {@link #getFrameTime() frame time} reported
* during this callback may be updated to reflect delays that occurred while
* traversals were in progress in case heavy layout operations caused some frames
* to be skipped. The frame time reported during this callback provides a better
* estimate of the start time of the frame in which animations (and other updates
* to the view hierarchy state) actually took effect.
* @hide
*/
public static final int CALLBACK_COMMIT = 4;
上述注釋解釋的很清楚:
事件值 | 解釋說明 |
---|---|
CALLBACK_INPUT | 輸入事件别瞭,第一個執(zhí)行 |
CALLBACK_ANIMATION | 動畫,在CALLBACK_INSETS_ANIMATION前執(zhí)行 |
CALLBACK_INSETS_ANIMATION | 輸入和動畫之后株憾,TRAVERSAL之前 |
CALLBACK_TRAVERSAL | 處理布局和繪制蝙寨,運行在其他異步消息之后 |
CALLBACK_COMMIT | 提交,最后執(zhí)行 |
第二個參數(shù)需要的是一個Runnable
第三個參數(shù)為Object token
我們在ViewRootImpl中調(diào)用scheduleTraversals()方法中:
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
第一個參數(shù)傳入的是CALLBACK_TRAVERSAL(布局和繪制)嗤瞎。
第二個參數(shù)傳入的是mTraversalRunnable墙歪,上面已經(jīng)說過,就是一個實現(xiàn)了Runnable接口的TraversalRunnable類贝奇。
第三個參數(shù)傳入的是null虹菲。
接著往下分析,可以看到在Choreographer類中又調(diào)用了
//注意這里傳入的delayMillis是0
postCallbackDelayed(callbackType, action, token, 0);
接著看該方法:
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
//判斷傳入的Runnable是否為null掉瞳,如果為null毕源,拋異常
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
//判斷傳入的callbackType是否正常(是否滿足該類中定義的五個值)
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
//接著往下調(diào)用
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
通過一些判斷之后浪漠,接著往下走:
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
//由于傳入的delayMillis是0,所以dueTime == now
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);
}
}
}
上述代碼中霎褐,mCallbackQueues是Choreographer類中的一個數(shù)組址愿,初始化是在Choreographer類的構(gòu)造方法中:
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
mCallbackQueues的長度是5,并且為數(shù)組中的每一個元素都新建了一個CallbackQueue冻璃∠煳剑看下CallbackQueue,CallbackQueue是Choreographer類的一個內(nèi)部類:
private final class CallbackQueue {
private CallbackRecord mHead;
public boolean hasDueCallbacksLocked(long now) {
return mHead != null && mHead.dueTime <= now;
}
public CallbackRecord extractDueCallbacksLocked(long now) {
......
}
@UnsupportedAppUsage
public void addCallbackLocked(long dueTime, Object action, Object token) {
......
}
public void removeCallbacksLocked(Object action, Object token) {
......
}
}
可以看到省艳,在CallbackQueue中存儲的是CallbackRecord娘纷,看下CallbackRecord類:
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();
}
}
}
CallbackRecord類中有一個成員變量是next,指向的是下一個CallbackRecord拍埠,因此在CallbackQueue中存儲的是一個鏈式結(jié)構(gòu)的CallbackRecord失驶。注意這里的run()方法,下面會用到枣购。
回到postCallbackDelayedInternal()方法中繼續(xù)看嬉探,我們現(xiàn)在傳入的delayMillis是0,因此dueTime == now,所以會執(zhí)行scheduleFrameLocked(now)方法棉圈。那如果走到else呢涩堤?答案是依然會執(zhí)行scheduleFrameLocked(now)方法。
簡單看下:
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
如果delayMillis不是0分瘾,那么會使用mHandler發(fā)送一個下次胎围,該Message的what是 MSG_DO_SCHEDULE_CALLBACK 。mHandler指向的是Choreographer類中的FrameHandler德召,我們看下FrameHandler的代碼:
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;
}
}
}
當(dāng) msg.what = MSG_DO_SCHEDULE_CALLBACK 的時候白魂,執(zhí)行的是doScheduleCallback(msg.arg1),看下該方法:
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
可以看到這里依然會走到scheduleFrameLocked(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);
}
}
}
可以看到肴掷,方法進來之后敬锐,先判斷了mFrameScheduled是否為true,此時不是true呆瞻,因此進入方法體中台夺,并且將mFrameScheduled設(shè)置為true。緊接著判斷USE_VSYNC是否為true痴脾,這個參數(shù)指的是是否開啟了VSYNC颤介,我們通過查資料可以知道在Android 4.1 之后默認開始了VSYNC,因此如果當(dāng)前Android 系統(tǒng)是4.1以后,那么會走if里面的邏輯买窟,反之則會調(diào)用else里面的邏輯丰泊。這里稍微看下4.1之前的邏輯,即不使用VSYNC信號始绍,則會直接發(fā)送一個 what = MSG_DO_FRAME的Message瞳购,通過上面FrameHandler的代碼可以知道,會調(diào)用doFrame方法亏推。doFrame方法我們下面再分析学赛。這里只要知道,如果沒有使用VSYNC信號吞杭,則會直接調(diào)用doFrame方法即可盏浇。
如果使用VSYNC信號,即進入if中芽狗,這里會判斷绢掰,isRunningOnLooperThreadLocked(),該方法內(nèi)部根據(jù)Looper判斷會否在原線程。
private boolean isRunningOnLooperThreadLocked() {
return Looper.myLooper() == mLooper;
}
如果是在原線程童擎,則調(diào)用scheduleVsyncLocked()方法滴劲,否則發(fā)送一個 what = MSG_DO_SCHEDULE_VSYNC 的Message,繼續(xù)看FrameHandler的代碼發(fā)現(xiàn)顾复,如果what = MSG_DO_SCHEDULE_VSYNC班挖,會調(diào)用doScheduleVsync()方法
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
在該方法中,因為在scheduleFrameLocked()方法中我們已經(jīng)將mFrameScheduled設(shè)置為true芯砸,因此會執(zhí)行scheduleVsyncLocked()方法萧芙。
到這里我們已經(jīng)可以看到FrameHandler的作用了:發(fā)送異步消息。因為我們之前已經(jīng)設(shè)置了同步屏障假丧,因此該異步消息會被執(zhí)行双揪。同時,如果任務(wù)是延遲的任務(wù)包帚,發(fā)送延遲消息盟榴。如果不在原線程,通過mHandlder發(fā)送消息到原線程.如果沒有開啟VSYNC的直接調(diào)用doFramen方法婴噩。
接著來看下scheduleVsyncLocked()方法。
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
mDisplayEventReceiver是FrameDisplayEventReceiver的實例羽德,我們看下FrameDisplayEventReceiver几莽。
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource);
}
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
......
}
@Override
public void run() {
......
}
}
可以看到,F(xiàn)rameDisplayEventReceiver繼承自DisplayEventReceiver宅静,并實現(xiàn)了Runnable接口章蚣。實現(xiàn)了兩個方法,onVsync()和run(),其中onVsync方法來自DisplayEventReceiver,run()方法來自Runnable接口纤垂。mDisplayEventReceiver是在Choreographer的構(gòu)造方法中創(chuàng)建出來的矾策,找到對應(yīng)的代碼:
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
......
}
可以看到,如果沒有使用VSYNC峭沦,則mDisplayEventReceiver為null贾虽,反之則創(chuàng)建。在創(chuàng)建的時候吼鱼,傳入了當(dāng)前的looper蓬豁。在該類中沒有找到scheduleVsync()方法,那我們就去父類中去找菇肃,先看下父類的構(gòu)造方法
public DisplayEventReceiver(Looper looper, int vsyncSource) {
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource);
mCloseGuard.open("dispose");
}
可以看到地粪,在父類的構(gòu)造方法中,先判斷了looper是否為null琐谤。然后從looper中獲取到MessageQueue賦值給mMessageQueue蟆技,然后調(diào)用了一個native的方法,并將返回值賦值給了mReceiverPtr斗忌。我們看下父類的scheduleVsync()方法质礼。
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
在該方法中,先判斷了mReceiverPtr是否為0飞蹂。這里應(yīng)該就是在構(gòu)造方法中几苍,如果調(diào)用nativeInit方法成功的話,值就不是0陈哑。因此如果mReceiverPtr不是0妻坝,會調(diào)用nativeScheduleVsync(mReceiverPtr)。該方法也是一個native方法惊窖,這里就不往下看了刽宪,我們只需要知道VSYNC信號的接收回調(diào)是onVsync()方法。因此我們繼續(xù)看onVsync方法界酒。在DisplayEventReceiver中圣拄,onVsync是一個空實現(xiàn),因此我們回到FrameDisplayEventReceiver類的onVsync方法
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
......
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
上面的直接省略毁欣,我們看下通過mHandler發(fā)送消息庇谆,可以看到,這里創(chuàng)建Message消息的時候凭疮,傳入的是this饭耳,因為FrameDisplayEventReceiver實現(xiàn)了Runnable接口,因此在handler處理消息的時候回調(diào)run方法执解,在run方法中寞肖,調(diào)用了doFramen方法。另外,這里調(diào)用了msg.setAsynchronous(true)新蟆,通過handler發(fā)送到MessageQueue中的是一個異步消息觅赊,我們前面已經(jīng)post了一個同步屏障,因此該異步消息會被調(diào)用琼稻。
注意吮螺,在上面我們提到了,在Android 4.1之前欣簇,也會走到doFramen方法中去规脸。
下面來看下doFrame方法
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();
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);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
可以看出,在doFrame中依次執(zhí)行了doCallbacks方法熊咽,傳入的類型是我們上面提到的Choreographer類中定義好的五種類型莫鸭。看下doCallbacks方法:
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) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
可以看到横殴,首先根據(jù)指定的任務(wù)類型被因,從mCallbackQueues中查找需要執(zhí)行的CallbackRecord,如果是null衫仑,直接return了梨与。緊接著,遍歷執(zhí)行CallbackRecord中的所有任務(wù)文狱,回調(diào)CallbackRecord的run 方法粥鞋。
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
因為我們在上面往CallbackRecord中add任務(wù)的時候,傳入的token是null瞄崇,所以這里直接走到了action.run()方法呻粹,而action是前面提到的TraversalRunnable,所以直接調(diào)用了TraversalRunnable類的run方法苏研,然后調(diào)用了doTraversal()等浊,在doTraversal()方法中,開始真正的繪制流程摹蘑〕镅啵看下doTraversal方法:
void doTraversal() {
if (mTraversalScheduled) {
//標記位設(shè)為false
mTraversalScheduled = false;
//從MessageQueue中移除同步屏障消息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//真正開始繪制流程
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
在調(diào)用performTraversals之前,調(diào)用了mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier),在這里就把之前我們添加的同步屏障從MessageQueue中移除衅鹿,從而使MesssageQueue中next()方法可以正常取到同步消息撒踪。
那么什么時候token == FRAME_CALLBACK_TOKEN成立呢?我們在Choregrapher類中查找FRAME_CALLBACK_TOKEN發(fā)現(xiàn)大渤,在調(diào)用postFrameCallback的時候制妄,會調(diào)用postFrameCallbackDelayed
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
在這里傳入了FRAME_CALLBACK_TOKEN。那么該方法的執(zhí)行流程呢兼犯?可以看到,在這個方法中也是調(diào)用到了postCallbackDelayedInternal方法,只不過傳入了token參數(shù)切黔。并且這里傳入的是action是FrameCallback砸脊,F(xiàn)rameCallback中只有一個doFrame方法。postFrameCallback通常情況下用來計算丟幀情況纬霞。
使用一張圖來梳理一下在整個Choreographer中的流程
在此感謝下面參考文章的作者凌埂,在沒有看到該作者的文章之前,也看過這個繪制流程的源碼诗芜,但總是看不進去瞳抓,看一點忘一點。后來看到這篇文章之后伏恐,結(jié)合文章中所介紹的內(nèi)容再去看源碼孩哑,靜下心來,慢慢的就看明白了翠桦,特此感謝横蜒。其實我們學(xué)習(xí)就是這樣了,找到一個自己喜歡的學(xué)習(xí)方法销凑,學(xué)習(xí)就會變得有樂趣丛晌。想要成長起來,源碼是繞不開的斗幼,通過看源碼澎蛛、理解源碼能夠使我們加深對于整個Android系統(tǒng)的理解,從而讓自己更上一層樓蜕窿。