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

2024年 5月 22日 55.9k 0

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

  • 视图绘制:负责调用View树的绘制流程,包括测量(measure)、布局(layout)和绘制(draw)等操作。监听View树的改变,并根据需要触发相应的绘制操作,确保界面的及时更新。
  • 事件分发:负责将输入的各种事件(如触摸事件、按键事件等)分发给正确的View处理。根据触摸事件的位置信息,逐层遍历View树,找到最合适的View来处理事件。将处理结果返回给系统,以便进行后续的处理,如滚动、点击等。
  • 窗口管理:承担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();
        }
    }

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

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

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论