View的绘制还有什么好聊的
前面我们已经减少了view的绘制流程了,很多同学都知道,面试的时候面试官问你:view的绘制流程是什么?绝大部分同学都会脱口而出:onMeasure()->onLayout()-onDraw(),然后把一切的流程给介绍出来,就像我上一篇文章所写的Android View 绘制流程。但是按照现在的android就业形势,如果只会这些“表面”的东西难免会让人缺少眼前一亮的感觉。那么今天我们就深入的了解一下完整的View绘制
谁调用了performTraversals()?
我们知道onMeasure、onLayout、onDraw这几个方法对应的就是ViewRootImp.java里面的performMeasure、performLayout、performDraw方法。这几个方法的入口就是performTraversals()。那么谁调用了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方法调用
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
doTraversal()方法是由TraversalRunnable方法调用。那么我们只需要看到谁调用了mTraversalRunnable即可
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
好了,调用链很明确了,就是scheduleTraversal方法触发了View的绘制流程。这个方法有很多的调用地方,最经典的就是View的invalidate()方法了。
View的invalidate流程分析
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
重中之重scheduleTraversals()
我们看一下scheduleTraversal方法的调用
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
这个方法非常的重要。Choreographer的作用就是协调input时间、动画和绘制的时间。其实如果大家知道vsync的话,那么我们就可以说,choreography就是监听vsync的心跳时间的callback,vsync会每隔16.7ms回调一次。我们看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);
}
}
}
我们可以看到发送了一个MSG_DO_SCHEDULE_CALLBACK的消息,再继续往下跟
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);
}
}
这里就是调用native方法去触发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方法是协调input、动画、和绘制流程的管理者。这里我们可以做一个引申,为什么android系统可以有动画,每一个动画的执行粒度和区间值是怎么保存和触发的,其实都是跟这个vsync相关联。
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
这里就回调会ViewRootImp.java里面的TraversalRunnable方法中
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
这里就开始和前面的分析流程形成了闭环。
自此上层的View绘制流程就分析完毕了。关于Vsync我们会在接下来的文章继续做分析,待更~~