ViewRootImpl如何负责管理绘制视图树和刷新界面

开发 前端
mHandler是当前主线程的handler,当接收到onVsync信号的时候,将自己封装到Message中,等到Looper处理,最后Looper处理消息的时候就会调用run方法最终从mCallbackQueues取回之前添加的任务再执行run方法,也就是TraservalRunnable的run方法。

ViewRootImpl位于视图层次结构的顶部,负责View和WindowManager之间的通信。

  1. 视图绘制:负责调用View树的绘制流程,包括测量(measure)、布局(layout)和绘制(draw)等操作。监听View树的改变,并根据需要触发相应的绘制操作,确保界面的及时更新。
  2. 事件分发:负责将输入的各种事件(如触摸事件、按键事件等)分发给正确的View处理。根据触摸事件的位置信息,逐层遍历View树,找到最合适的View来处理事件。将处理结果返回给系统,以便进行后续的处理,如滚动、点击等。
  3. 窗口管理:承担Android窗口管理的一部分职责。负责创建和管理窗口,将窗口与ViewRootImpl进行绑定。当窗口需要显示或隐藏时,ViewRootImpl会相应地调整界面的显示状态。

ViewRootImpl关联了多个类和接口,IWindowSession、Choreographer及其FrameCallback接口等。IWindowSession用于客户端和WindowManagerService之间进行窗口管理操作的接口,允许ViewRootImpl与WindowManagerService进行通信,执行如添加、删除、更新窗口等操作。

scheduleTraversals

scheduleTraversals()方法负责将一次视图遍历(traversal)排期到其调度计划中,但并不会立即执行遍历操作。方法被许多操作所调用,比如当视图的大小、位置等属性发生变化时,或者当调用requestLayout()、invalidate()等方法时,都会触发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);
    }
    ...
}

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);
    }
}

当VSYNC信号到来时(VSYNC信号是Android系统中用于同步屏幕刷新的信号),系统会从待执行队列中取出对应的scheduleTraversals()操作,并将其加入到主线程的消息队列中。然后,主线程会从消息队列中取出并执行这个操作,进而触发视图的测量、布局和绘制流程。

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();
}

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);
    }
}

在这个过程中,performTraversals()方法会被调用。方法会执行实际的测量、布局和绘制操作。首先会调用measureHierarchy()方法进行测量,然后调用performLayout()方法进行布局,最后调用draw()方法进行绘制。这些操作会按照顺序执行,以确保视图能够正确地显示在屏幕上。最终通过nativeScheduleVsync()原生方法通知屏幕进行绘制。

performTraversals

performTraversals()方法负责启动视图的测量(measure)、布局(layout)和绘制(draw)流程。当需要创建视图、视图参数改变或界面需要刷新时,可能会从根视图DecorView开始重新进行测量、布局和绘制,这时就会调用到performTraversals()方法。

private void performTraversals() {
    ...
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    ... 
    performDraw();
}

void doTraversal() {
      //防止重入
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        //移除同步屏障
        mHandler.getLooper().getQueue()
                    .removeSyncBarrier(mTraversalBarrier);
        performTraversals();
    }
}

图片图片

  1. 「测量(Measure)」:在这个阶段,系统会遍历整个视图树,计算每个视图的大小。这个过程中会用到MeasureSpec,每个MeasureSpec都包含了一个测量模式和测量大小,测量模式主要有三种:EXACTLY(父视图已经确定了子视图的确切大小)、AT_MOST(子视图的大小有一个最大值限制)和UNSPECIFIED(父视图对子视图的大小没有要求)。
  2. 「布局(Layout)」:在测量完成后,系统会为每个视图确定其在屏幕上的精确位置。这个过程中,父视图会根据测量阶段得到的子视图大小以及自身的布局参数,计算出子视图应该放置的位置。
  3. 「绘制(Draw)」:系统会遍历整个视图树,根据每个视图的绘制参数(如颜色、形状等)将其绘制到屏幕上。这个过程中,视图会按照其在视图树中的层次顺序进行绘制,先绘制父视图,再绘制子视图。
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);
    }
}

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);
        }
    }
}

//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);
}

// 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();
    }
}

mHandler是当前主线程的handler,当接收到onVsync信号的时候,将自己封装到Message中,等到Looper处理,最后Looper处理消息的时候就会调用run方法最终从mCallbackQueues取回之前添加的任务再执行run方法,也就是TraservalRunnable的run方法。最终触发performTraversals方法进行界面刷新。

责任编辑:武晓燕 来源: 沐雨花飞蝶
相关推荐

2009-12-25 18:06:11

WPF刷新界面

2009-08-31 09:13:00

UbuntuNetBook Rem界面

2012-07-17 09:53:02

2009-07-10 08:50:35

微软Windows 7界面

2010-04-15 09:47:02

2012-06-18 10:57:25

Windows 8操作系统

2011-12-29 10:13:48

FirefoxAndroid版

2017-05-12 09:29:42

操作系统Windows 10 win 10 NEON

2010-08-05 09:17:17

MeeGo界面

2011-04-14 13:30:55

webOS 3.0webOS惠普

2012-05-11 16:11:50

Visual Stud

2009-12-23 20:45:09

Firefox全新界面

2022-07-06 09:54:46

微软Windows 11

2023-05-04 06:31:45

OneDrive微软

2009-02-12 15:18:59

2011-08-18 09:30:39

金山Kingsoft Of

2012-08-15 09:26:30

SkyDriveOutlook.com

2012-03-26 11:00:10

Visual Stud微软开发

2023-05-04 09:44:17

开源FydeOS

2011-05-11 09:24:27

Symbian^3Symbian诺基亚
点赞
收藏

51CTO技术栈公众号