Android ViewRootImpl

本文主要分析两个问题:
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();
    }
}    

总结上面的分析,调用流程如下图所示如下:

未命名.png

三、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()方法。

你可能感兴趣的:(Android ViewRootImpl)