Android窗口管理5 理解ViewRootImpl

一 概述

ViewRootImpl 是一个视图层次结构的顶部,可以理解为一个 Window 中所有 View 的根 View 的管理者(但 ViewRootImpl 不是 View,只是实现了 ViewParent 接口),实现了 View 和 WindowManager 之间的通信协议,实现的具体细节在 WindowManagerGlobal 这个类中。

在这里插入图片描述

简单来说 ViewRootImpl 是 View 与 WindowManager 之间联系的桥梁,作用总结如下:

1.将 DecorView 传递给 WindowManagerSerive
2.完成 View 的绘制过程,包括 measure、layout、draw 过程
3.向 DecorView 分发收到的用户发起的 event 事件,如按键,触屏等事件。

其中,ViewRootImpl 中包含了两个需要重点关注的内部类:

  • final class ViewRootHandler extends Handler 用于向 DecorView 分发事件
  • static class W extends IWindow.Stub

W 是 ViewRootImp l的一个嵌入类,也是一个 Binder 服务。通过 mWindowSession.addToDisplay 函数传入 WMS,用来在 WMS 中通过 Binder 回调。

二 将Window传递给WMS

public void setView(View view, WindowManager.LayoutParams attrs,
        View panelParentView) {
   
        synchronized (this) {
   
            if (mView == null) {
   
                mView = view;
               ......
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();//见下节介绍
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.
                        LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
   
                    mInputChannel = new InputChannel();//生成InputChannel
                }
                ......
                try {
   
                    ......
                    //调用mWindowSession.addToDisplay通过binder调用到WMS
                    //实现对Window的真正的添加,这里的mWindow为 W 对象
                    res = mWindowSession.addToDisplay(mWindow, mSeq, ......);
                    setFrame(mTmpFrame);
                } catch (RemoteException e) {
   
                    ......
                } finally {
   
                    ......
                }
                ......               
                if (mInputChannel != null) {
   
                    if (mInputQueueCallback != null) {
   
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    //用于输入事件的接收
                    mInputEventReceiver = 
                    new WindowInputEventReceiver(
                    mInputChannel, Looper.myLooper());
                }
               ......
            }
        }
    }

三 View的绘制

3.1 requestLayout

frameworks/base/core/java/android/view/View.java

public void requestLayout() {
   
    if (mMeasureCache != null) mMeasureCache.clear();
	// 步骤1
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
   
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot != null && viewRoot.isInLayout()) {
   
            if (!viewRoot.requestLayoutDuringLayout(this)) {
   
                return;
            }
        }
        mAttachInfo.mViewRequestingLayout = this;
    }

	// 步骤2
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    mPrivateFlags |= PFLAG_INVALIDATED;
    if (mParent != null && !mParent.isLayoutRequested()) {
   
        mParent.requestLayout();
    }
    if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
   
        mAttachInfo.mViewRequestingLayout = null;
    }
}

调用时机:
当某些变化导致原有 View 树的 layout 发生变化,可以调用 requestLayout 重新布局 View 树。

1.关于 requestLayout 的叠加

  • 尽量不要在 layout 过程中又调用 requestLayout
  • 如果非要叠加调用 requestLayout,则将当前作出该请求的 View 先放入 mLayoutRequesters 列表,等到调用 ViewRootImpl#performLayout 的时候再取出 mLayoutRequesters 列表依次调用各个 View#requestLayout。如果当前正在处理 mLayoutRequesters 列表,则不要再次触发 requestLayout

2.mParent.requestLayout()

  • 我们先搞清楚 mParent 是啥?
    mParent 的类型是 ViewParent,通过代码溯源 View#assignParent,可以得出如下结论:
    DecorView (即根 View) 对应的 mParent 是 ViewRootImpl,普通子 View (非根 View) 对应的 mParent 是子 View 的父 View (即 ViewGroup)

  • requestLayout 调用过程?
    该过程是一个从子 View -> 父 View -> DecorView -> ViewRootImpl 层层往上的递归调用过程

3.2 invalidate

frameworks/base/core/java/android/view/View.java

public void invalidate() {
   
    invalidate(true);
}

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) {
   
    if (skipInvalidate()) {
   
        return;
    }
    ......
    // 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);
    }
    ......
}

frameworks/base/core/java/android/view/ViewGroup.java

@Override
public final void invalidateChild(View child, final Rect dirty) {
   
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null && attachInfo.mHardwareAccelerated) {
   
        // HW accelerated fast path
        onDescendantInvalidated(child, child);
        return;
    }

    ViewParent parent = this;
    if (attachInfo != null) {
   
        final int[] location = attachInfo.mInvalidateChildLocation;
        location[CHILD_LEFT_INDEX] = child.mLeft;
        location[CHILD_TOP_INDEX] = child.mTop;
		......
        do {
   
            ......
            parent = parent.invalidateChildInParent(location, dirty);
            ......
        } while (parent != null);
    }
}

1.调用时机:
当前 View 树需要重绘时。如果当前 View 可见,则会调到 onDraw 方法。
该方法必须在 UI 线程调用。

2.View#invalidate 的调用逻辑:
从子 View -> 父 View -> DecorView -> ViewRootImpl 从子到父层层调用。

  • 硬件加速
    ViewGroup#onDescendantInvalidated -> …-> DecorView#onDescendantInvalidated -> ViewRootImpl#onDescendantInvalidated

  • 非硬件加速
    ViewGroup#invalidateChild -> ViewGroup#invalidateChildInParent -> … -> DecorView#invalidateChildInParent -> ViewRootImpl#invalidateChildInParent

3.skipInvalidate 逻辑

private boolean skipInvalidate() {
   
    return (mViewFlags & VISIBILITY_MASK) != VISIBLE && 
        mCurrentAnimation == null &&
            (!(mParent instanceof ViewGroup) ||
                    !((ViewGroup) mParent).isViewTransitioning(this));
}

如果当前 View 不可见并且当前没有动画时,则不会 invalidate 执行重绘.

3.3 postInvalidate

frameworks/base/core/java/android/view/View.java

public void postInvalidate() {
   
    postInvalidateDelayed(0);
}

public void postInvalidateDelayed(long delayMilliseconds) {
   
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
   
        attachInfo.mViewRootImpl.dispatchInvalidateDelayed(
            this, delayMilliseconds);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值