View的繪制還有什么好聊的
前面我們已經(jīng)減少了view的繪制流程了葱跋,很多同學都知道娱俺,面試的時候面試官問你:view的繪制流程是什么废麻?絕大部分同學都會脫口而出:onMeasure()->onLayout()-onDraw()脑溢,然后把一切的流程給介紹出來,就像我上一篇文章所寫的Android View 繪制流程屑彻。但是按照現(xiàn)在的android就業(yè)形勢验庙,如果只會這些“表面”的東西難免會讓人缺少眼前一亮的感覺。那么今天我們就深入的了解一下完整的View繪制
誰調(diào)用了performTraversals()社牲?
我們知道onMeasure粪薛、onLayout、onDraw這幾個方法對應的就是ViewRootImp.java里面的performMeasure搏恤、performLayout违寿、performDraw方法。這幾個方法的入口就是performTraversals()熟空。那么誰調(diào)用了performTraversals()方法呢藤巢?我們一步一步往下跟。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals()方法由doTraversal方法調(diào)用
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
doTraversal()方法是由TraversalRunnable方法調(diào)用息罗。那么我們只需要看到誰調(diào)用了mTraversalRunnable即可
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
好了,調(diào)用鏈很明確了,就是scheduleTraversal方法觸發(fā)了View的繪制流程孩革。這個方法有很多的調(diào)用地方锅移,最經(jīng)典的就是View的invalidate()方法了。
View的invalidate流程分析
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
重中之重scheduleTraversals()
我們看一下scheduleTraversal方法的調(diào)用
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
這個方法非常的重要坤学。Choreographer的作用就是協(xié)調(diào)input時間、動畫和繪制的時間杂穷。其實如果大家知道vsync的話,那么我們就可以說趴拧,choreography就是監(jiān)聽vsync的心跳時間的callback兄渺,vsync會每隔16.7ms回調(diào)一次瞎饲。我們看choreography源碼
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) {
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);
}
}
}
我們可以看到發(fā)送了一個MSG_DO_SCHEDULE_CALLBACK的消息疟呐,再繼續(xù)往下跟
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;
}
}
}
這里終于可以看到所有的消息的處理了鲁冯,也包括前面提到的vsync流程
/**
* Schedules a single vertical sync pulse to be delivered when the next
* display frame begins.
*/
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);
}
}
這里就是調(diào)用native方法去觸發(fā)vsync了序无。我們重點看下doFrame()方法
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);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
這里也就符合我們開頭所說的:Choreography方法是協(xié)調(diào)input、動畫、和繪制流程的管理者塘秦。這里我們可以做一個引申,為什么android系統(tǒng)可以有動畫菱皆,每一個動畫的執(zhí)行粒度和區(qū)間值是怎么保存和觸發(fā)的须误,其實都是跟這個vsync相關聯(lián)。
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
這里就回調(diào)會ViewRootImp.java里面的TraversalRunnable方法中
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
這里就開始和前面的分析流程形成了閉環(huán)篷店。
自此上層的View繪制流程就分析完畢了疲陕。關于Vsync我們會在接下來的文章繼續(xù)做分析携茂,待更~~