本文主要分析两个问题:
1、为什么View 的绘制流程是从 ViewRootImpl 的performTraversals()方法开始的?
2、View 的invalidate方法是怎么触发到ViewRootImpl 的performTraversals()方法的。
在阅读本文前,最好先了解window的添加过程,Android消息处理机制 和 View 的绘制流程。推荐先阅读以下文章:
Android Window和WindowManager
Android-消息机制
Android View 的绘制流程
一、ViewRootImpl的定义
/**
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* detail of {@link WindowManagerGlobal}.
*
* {@hide}
*/
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
android 源码注释的意思是:ViewRootImpl是视图层次结构的顶部,实现 View 和 WindowManager 之间所需的协议。是 WindowManager Global 的内部实现中重要的组成部分。
二、ViewRootImpl performTraversals()
View 的绘制流程是从 ViewRootImpl 的performTraversals()方法开始的,那到底是哪里调用了performTraversals()方法呢,下面我们分析一下:
1.私有属性的performTraversals()方法肯定是在内部调用起来的,经过搜索找到是doTraversal()方法调用了。
private void performTraversals() {
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
performDraw();
}
void doTraversal() {
//防止重入
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障
mHandler.getLooper().getQueue()
.removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
2.接着找到了,调用了doTraversal() 的TraversalRunnable 类
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
3.内部只有一个地方实例化了TraversalRunnable 的实例mTraversalRunnable ,查到到两个方法内都调用了mTraversalRunnable ,明显 scheduleTraversals 是主动触发这个 Runnable 。这就表明调用了scheduleTraversals ()函数的地方都主动触发了view的刷新。
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
if (!mTraversalScheduled) {
//移除同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
void unscheduleTraversals() {
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
4.接着我们看一下 mChoreographer.postCallback 做了什么
//Choreographer
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
...
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
....
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
//把 任务添加到了mCallbackQueues 回调里面去,等待回调执行。
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
//now=0 ,走进scheduleFrameLocked()方法内
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);
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
//这里判断,当前执行的线程是否是创建该Choreographer的线程,如果是直接执行。否则通过handler 发送到 创建该Choreographer的线程去执行。
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
//这条message 最后处理还是调用到了scheduleVsyncLocked方法
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);
}
}
}
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
可以看到,最后后走进了scheduleVsyncLocked()方法内。
5.mDisplayEventReceiver 的类 是 FrameDisplayEventReceiver,继承自
DisplayEventReceiver 。
/**
* 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);
}
}
最后走到这里就没了,那么这个方法是做了什么呢,这个方法的注释是这个意思:安排在下一个显示帧开始时传送单个垂直同步脉冲。意思就是,调用了这个方法可以收到系统传送过来的垂直同步脉冲信号。Android系统每隔16ms就会发送一个VSYNC信号(VSYNC:vertical synchronization 垂直同步,帧同步),触发对UI进行渲染。这个垂直同步信对于应用来说了,只有了订阅了监听,才能收到。而且是订阅一次,收到一次。
6.既然是在这个类里面订阅垂直同步信号的,那回调也应该在这里。于是找到了以下方法。
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}
/**
* Called when a vertical sync pulse is received.
* The recipient should render a frame and then call {@link #scheduleVsync}
* to schedule the next vertical sync pulse.
*
* @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
* timebase.
* @param builtInDisplayId The surface flinger built-in display id such as
* {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}.
* @param frame The frame number. Increases by one for each vertical sync interval.
*/
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
}
native code 调用到 onVsync,这个方法的注释解释如下:当接收到垂直同步脉冲时调用。接收者应该渲染一个帧,然后调用 {@link scheduleVsync} 来安排下一个垂直同步脉冲。
这个方法的具体实现在前面分析到的FrameDisplayEventReceiver 类里面。
//Choreographer内部类DisplayEventReceiver,重写了onVsync方法
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
// 设置成异步消息
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
这里可以看到,其实mHandler就是当前主线程的handler,当接收到onVsync信号的时候,将自己封装到Message中,等到Looper处理,最后Looper处理消息的时候就会调用run方法,这里是Handler的机制,不做解释。
7.最后,如下图调用所示,最终从mCallbackQueues取回之前添加的任务再执行run方法,也就是TraservalRunnable的run方法。
// Choreographer
void doFrame(long frameTimeNanos, int frame) {
...
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
// 从mCallbackQueues取出
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
}
// CallbackRecord
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
// 这里也即是调用了TraservalRunnable的run方法,也即是三个绘制流程
((Runnable)action).run();
}
}
总结上面的分析,调用流程如下图所示如下:
三、ViewRootImpl scheduleTraversals()
上面我们分析到只要调用了ViewRootImpl 的scheduleTraversals ()方法,最终就能调用了ViewRootImpl 的performTraversals()来开始绘制。那肯定是我们常调用的view刷新的接口,经过一系列的方法调用,最终调用了ViewRootImpl 的scheduleTraversals ()方法。下面我们分析一下常用的View 的 invalidate()接口是怎么调用到了ViewRootImpl 的scheduleTraversals ()方法。
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
...
// Propagate the damage rectangle to the parent view.
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);
//调用父容器的方法,向上传递事件
p.invalidateChild(this, damage);
}
...
}
}
可以看出,invalidate有多个重载方法,但最终都会调用invalidateInternal方法,在这个方法内部,进行了一系列的判断,判断View是否需要重绘,接着为该View设置标记位,然后把需要重绘的区域传递给父容器,即调用父容器的invalidateChild方法。
接着我们看ViewGroup#invalidateChild:
public final void invalidateChild(View child, final Rect dirty) {
.....
ViewParent parent = this;
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
} else if (parent instanceof ViewRootImpl) {
((ViewRootImpl) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null) {
if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
view.getSolidColor() == 0) {
opaqueFlag = PFLAG_DIRTY;
}
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
}
}
//调用ViewGrup的invalidateChildInParent,如果已经达到最顶层view,则调用ViewRootImpl
//的invalidateChildInParent。
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
}
}
由于不断向上调用父容器的方法,到最后会调用到ViewRootImpl的invalidateChildInParent方法,我们来看看它的源码,ViewRootImpl#invalidateChildInParent:
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
....
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
....
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
return null;
}
最后调用了scheduleTraversals方法,触发View的工作流程。至此,我们已经完整地分析了一次调用View 的 invalidate()方法到触发ViewRootImpl 的scheduleTraversals()方法。