WMS & View

707 阅读14分钟

基于Android R版本分析

WMS&View关系架构

WMS&View关系架构.png

View 树

例如在布局中给Activity设置一个布局xml,最顶层的布局LinearLayout就是view树的根,它包含的所有view都是该View树的节点,View树对应一个window;

View树.jpg

在Android系统中,几乎所有的UI元素都是基于View和ViewGroup创建的,ViewGroup和View是组合模式的典型应用;

  • View:一块可以用来进行绘画,可以处理输入事件进行交互的矩形区域;

    View是基本的控件元素,ViewManager接口定义了添加、删除View的接口addView、removeView;

  • ViewGroup:可以容纳View的矩形容器;

    ViewGroup实现了ViewParent的接口,因此可以作为容器管理View,同时ViewGroup又继承自View,可以被其他的ViewGroup管理。这样ViewGroup和View就可以组成上面的树状结构了;

view&viewgroup.png

View树创建

View树的创建方式有两种:

  • 通过Layout资源创建View树;
  • 通过代码动态创建View树;
Layout资源创建View

同样也有两种方式创建:

  • 通过LayoutInflater.inflater()方法加载Layout布局;
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.main, null);
  • 通过setContentView()方法加载Layout布局;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

通过Layout资源创建View树的优势是层次结构清晰,管理方便。同时View树中各个View的属性也可以在layout资源里进行设置。缺点是无法在应用运行时对View树结构进行变化;

代码动态创建View树

通过代码调用ViewGroup的addView()、removeView()等接口实现View树的创建;

TextView text = new TextView(this);
Button button = new Button(this);
​
FrameLayout frame = new FrameLayout(this);
frame.addView(text);
​
LinearLayout linear = new LinearLayout(this);
linear.setOrientation(LinearLayout.VERTICAL);
linear.addView(frame);
linear.addView(button);

Window

Window机制.png

在Android系统中,屏幕的抽象是DisplayContent,在屏幕的抽象上,通过不同的窗口,展示不同的应用程序页面和一些其他UI组件(Activity、Dialog、StatusBar等)。Window通过在Z轴叠放,从而实现了复杂的交互逻辑;

  • Window: 是一个处理顶级窗口外观和行为策略的抽象基类,它的具体实现是 PhoneWindow 类,PhoneWindow 对 View 进行管理;
  • ViewRootImpl:代表的是Android视图层次结构的顶部,是View和WindowManager之间的桥梁。Android的视图层次结构是树结构,ViewRootImpl实际上就是输的根节点的抽象,WindowManager通过根节点对象,来更新整个树结构上的View节点的内容;
  • Session:用于其他应用程序和WMS通信之间的通道,每个应用程序都会有一个Session,在WMS通过mSessions属性保存,它的类型为ArraySet;
  • WindowState:代表WMS管理的一个窗口,继承自WindowContainer,并实现了一些其他接口;
WindowToken & WindowState

WindowToken&WindowState.jpeg

  • WindowToken:WindowToken将同一应用组件(Activity、InputMethod、Wallpaper或者Dream)的窗口(WindowState)组织到一起;
class WindowToken extends WindowContainer<WindowState>

表明WindowToken是容器,其中存放WindowState;

WMS对窗口的管理过程中,用WindowToken指代一个应用组件。例如在进行窗口ZOrder排序的时候,同属于一个WindowToken的窗口(WindowState)会被排在一起,且其中定义的属性会影响到所有属于这个WindowToken的窗口;

Window add
public int addWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        InputChannel outInputChannel) {
    ...
    //1
    int res = mPolicy.checkAddPermission(attrs, appOp);
    ...
    synchronized(mWindowMap) {

        //2
        final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);

        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
            //3
            parentWindow = windowForClientLocked(null, attrs.token, false);

        }
        ...            
        //4
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);

        if (token == null) {                              
            final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
            //5
            token = new WindowToken(this, binder, type, false, displayContent,
                    session.mCanAddInternalSystemWindow);
        } 
        ...
        //6
        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], seq, attrs, viewVisibility, session.mUid,
                session.mCanAddInternalSystemWindow);
        ...
        //7
        mPolicy.adjustWindowParamsLw(win.mAttrs);
        ...
        if  (openInputChannels) {
            //8
            win.openInputChannel(outInputChannel);
        }
        ...
        //9
        mWindowMap.put(client.asBinder(), win);
        ...
        //10
        win.mToken.addWindow(win);
        ...
        //11
        displayContent.assignWindowLayers(false /* setLayoutNeeded */);

        //12
        if (focusChanged) {
            mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
        }
        mInputMonitor.updateInputWindowsLw(false /*force*/);

    }
    ...
    return res;

}
  1. 检查当前 Window 的 token 等权限合法性;
  2. 使用 RootWindowContainer 的子容器中获取一个 DisplayContent,如果子容器集合中不存在,则去获取一个,并添加到 child 集合中;
  3. 如果是 Dialog 等子窗口,则获取父窗口,没有就报找不到父窗口的异常;
  4. 使用 attr.token 去 displayContent 的键值对 mTokenMap 中获取对应的 WindowToken,WindowToken 中保存了一组 Window;
  5. 如果4中 WindowToken 为 null,则创建一个 WindowToken,传入 app 层传入的 attr.token 以及 displayContent 对象,内部会将这个创建的 WindowToken 保存到 displayContent 中;
  6. 创建一个 WindowState,并传入所有关于 Window 相关的属性,这样 WindowState 在 WMS 中就是以一个 Window 性质存在了、WindowState 构造过程中会将其添加到 WindowToken 中去;
  7. 根据 mPolicy 调整 window 的 attr 属性,mPolicy 的实现类是PhoneManagerPolicy;
  8. 执行 WindowState 的 openInputChannel,这里主要是打通和 Input 系统的通道,用于接收 IMS 的输入事件请求;
  9. 将客户端 app 层的 Window 对象和 WindowState 关联上,这样 WMS 就可以通过 Window 找到 WMS 中的 WindowState 对象;
  10. win.mToken 是前面创建的 WindowToken 对象,所以此处就是将WindowState 加入到 WindowToken 的子容器集合中;
  11. 分配窗口的层级,这里的 setLayoutNeeded 参数为 false,说明不需要进行 Layout 操作;

根据addWindow的逻辑,DisplayContent、WindowToken、WindowState之间的映射关系:

WindowToken&WindowState类图.png

  • WindowContainer:定义了一组可以直接或通过其子类以层次结构形式保存Window的类的常用功能;

主要就是创建了一个和 Window 一一对应的 WindowState 对象,并将 WindowState 插入到父容器 WindowToken 的子容器集合中,而 WindowToken 又保存在 DisplayContent 的键值对集合中;

WindowInsets

Insets:插入物,屏幕上除了App绘制的内容还有系统的插入物,例如:StatusBar、NavigationBar、IME(软键盘)等,这些系统UI和应用UI有可能出现冲突。Insets可以将所需要绘制的View从屏幕边缘向内移动到一个合适的位置;

public final class Insets implements Parcelable {
    public static final @NonNull Insets NONE = new Insets(0, 0, 0, 0);

    public final int left;
    public final int top;
    public final int right;
    public final int bottom;

    private Insets(int left, int top, int right, int bottom) {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }
    ……………………
}

Insets定义了窗口上下左右对其他SystemUI等系统UI的偏移;

在Android 中,Insets区域则由WindowInsets类表示;

dumpsys window
  WindowInsetsStateController
    InsetsState
      InsetsSource type=ITYPE_STATUS_BAR frame=[0,0][1440,171] visible=true
      InsetsSource type=ITYPE_NAVIGATION_BAR frame=[0,2792][1440,2960] visible=true
      InsetsSource type=ITYPE_TOP_GESTURES frame=[0,0][1440,171] visible=true
      InsetsSource type=ITYPE_BOTTOM_GESTURES frame=[0,2792][1440,2960] visible=true
      InsetsSource type=ITYPE_LEFT_GESTURES frame=[0,0][0,2960] visible=true
      InsetsSource type=ITYPE_RIGHT_GESTURES frame=[1440,0][1440,2960] visible=true
      InsetsSource type=ITYPE_TOP_TAPPABLE_ELEMENT frame=[0,0][1440,171] visible=true
      InsetsSource type=ITYPE_BOTTOM_TAPPABLE_ELEMENT frame=[0,2792][1440,2960] visible=true
      InsetsSource type=ITYPE_LEFT_DISPLAY_CUTOUT frame=[0,0][-2147483648,2960] visible=true
      InsetsSource type=ITYPE_TOP_DISPLAY_CUTOUT frame=[0,0][1440,171] visible=true
      InsetsSource type=ITYPE_RIGHT_DISPLAY_CUTOUT frame=[2147483647,0][1440,2960] visible=true
      InsetsSource type=ITYPE_BOTTOM_DISPLAY_CUTOUT frame=[0,2147483647][1440,2960] visible=true
      InsetsSource type=ITYPE_IME frame=[0,0][0,0] visible=false
    Control map:
    ITYPE_IME -> Window{c97004c u0 com.example.android.pictureinpicture/com.example.android.pictureinpicture.MainActivity}
    ITYPE_NAVIGATION_BAR -> Window{c97004c u0 com.example.android.pictureinpicture/com.example.android.pictureinpicture.MainActivity}
    ITYPE_STATUS_BAR -> Window{c97004c u0 com.example.android.pictureinpicture/com.example.android.pictureinpicture.MainActivity}
    InsetsSourceProviders map:
    ITYPE_IME -> 

WindowInsets

WindowInsets用于描述窗口内容的一组插入。简单就是用来获取系统控件的位置,然后来适配我们自己的View的位置;

public final class WindowInsets {

    // 
    private final Insets[] mTypeInsetsMap;
    private final Insets[] mTypeMaxInsetsMap;
    private final boolean[] mTypeVisibilityMap;
​
    @Nullable private Rect mTempRect;
    private final boolean mIsRound;
    @Nullable private final DisplayCutout mDisplayCutout;
​
    /**
     * In multi-window we force show the navigation bar. Because we don't want that the surface size
     * changes in this mode, we instead have a flag whether the navigation bar size should always
     * be consumed, so the app is treated like there is no virtual navigation bar at all.
     */
    private final boolean mAlwaysConsumeSystemBars;
​
    // 判断SystemInsets是否已经被使用
    private final boolean mSystemWindowInsetsConsumed;
    // 判断StableInsets是否已经被使用
    private final boolean mStableInsetsConsumed;
    private final boolean mDisplayCutoutConsumed;
​
    private final int mCompatInsetsTypes;
    private final boolean mCompatIgnoreVisibility;
    ……………………
}

在Android P版本中,WindowInsets还定义了三个变量(类型为Rect):

  • mSystemWindowInsets:代表着整个屏幕窗口上,状态栏,导航栏,输入法等系统窗口占用的区域;
  • mWindowDecorInsets:表示内容窗口下,被Android FrameWork提供的窗体,诸如ActionBar, TitleBar, ToolBar部分或全部覆盖区域,也可以简单理解为装饰区域,出去状态栏、导航栏等系统区域的区域;
  • mStableInsets:代表着整个屏幕窗口上,被系统UI部分或者全部覆盖的区域;

在Android R版本中,这个概念移除了上述定义;

WindowInsets.Type

WindowInsets.Type来表示不同的窗口装饰区域类型的,该类使用二进制整数的每一位来标识特定的窗口装饰区域类型

/**
  * Class that defines different types of sources causing window insets.
  */
public static final class Type {
​
    static final int FIRST = 1 << 0;
    static final int STATUS_BARS = FIRST;
    static final int NAVIGATION_BARS = 1 << 1;
    static final int CAPTION_BAR = 1 << 2;
​
    static final int IME = 1 << 3;
​
    static final int SYSTEM_GESTURES = 1 << 4;
    static final int MANDATORY_SYSTEM_GESTURES = 1 << 5;
    static final int TAPPABLE_ELEMENT = 1 << 6;
​
    static final int DISPLAY_CUTOUT = 1 << 7;
​
    static final int LAST = 1 << 8;
    static final int SIZE = 9;
    static final int WINDOW_DECOR = LAST;
​
    static int indexOf(@InsetsType int type) {
        switch (type) {
            case STATUS_BARS: // 状态栏
                return 0;
            case NAVIGATION_BARS: // 导航栏
                return 1;
            case CAPTION_BAR: // 标题栏
                return 2;
            case IME: // 软键盘
                return 3;
            case SYSTEM_GESTURES: // 系统手势
                return 4;
            case MANDATORY_SYSTEM_GESTURES: // 强制系统手势
                return 5;
            case TAPPABLE_ELEMENT:
                return 6;
            case DISPLAY_CUTOUT:
                return 7;
            case WINDOW_DECOR:
                return 8;
            default:
                throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
                                                   + " type=" + type);
        }
    }
    ……………………
​
    private Type() {
    }
​
    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, value = {STATUS_BARS, NAVIGATION_BARS, CAPTION_BAR, IME, WINDOW_DECOR,
                                  SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT, DISPLAY_CUTOUT})
    public @interface InsetsType {
    }
​
    /**
      * @return An insets type representing any system bars for displaying status.
      */
    // 这个就是我们常用的方法,在应用层中,通过WindowInsets.的方式获取需要调整的SystemWindow Type
    public static @InsetsType int statusBars() {
        return STATUS_BARS;
    }
}
InsetsSource

表示产生Insets的Window状态信息,例如 Insets 大小、是否可见等信息;

public class InsetsSource implements Parcelable {
​
    private final @InternalInsetsType int mType;
​
    /** Frame of the source in screen coordinate space */
    // Frame的Window窗口在屏幕坐标空间
    private final Rect mFrame;
    // 可见区域
    private @Nullable Rect mVisibleFrame;
    // 可见性状态
    private boolean mVisible;
​
    private final Rect mTmpFrame = new Rect();
}
InsetsSource type=ITYPE_STATUS_BAR frame=[0,0][1440,171] visible=true
  • mType:ITYPE_STATUS_BAR
  • mFrame:[0,0][1440,171]
  • mVisibleFrame:null
  • mVisible:true
InsetsSourceControl

对InsetsSource的控制者,用来控制Insets的产生者,内部持有控制动画的Leash;

InsetsSourceControl被 InsetsController 所持有,同样也是在 ViewRootImpl:: relayoutWindow 时从 WMS 中获得;

public class InsetsSourceControl implements Parcelable {
    // 指定了该Control的类型,映射到InsetsState.InternalInsetsType
    private final @InternalInsetsType int mType;
    // Insets窗口的父节点,可以用来控制窗口的属性
    private final @Nullable SurfaceControl mLeash;
    private final Point mSurfacePosition;
​
    public InsetsSourceControl(@InternalInsetsType int type, @Nullable SurfaceControl leash,
            Point surfacePosition) {
        mType = type;
        mLeash = leash;
        mSurfacePosition = surfacePosition;
    }
​
    public InsetsSourceControl(InsetsSourceControl other) {
        mType = other.mType;
        if (other.mLeash != null) {
            mLeash = new SurfaceControl(other.mLeash, "InsetsSourceControl");
        } else {
            mLeash = null;
        }
        mSurfacePosition = new Point(other.mSurfacePosition);
    }
}

InsetsSourceControl会根据Type类型的InsetsSource信息来控制动画的绘制逻辑;

InsetsSourceProvider

代表特定InsetsSource在server端的控制者,他被称作Provider是因为他提供InsetsSource给客户端(客户端通过InsetsSourceConsumer使用InsetsSource);

class InsetsSourceProvider {
​
    protected final DisplayContent mDisplayContent;
    // 代表持有的InsetsSource实例
    protected final @NonNull InsetsSource mSource;
    // 代表了对应的Window窗口,该WindowState和InsetsSource有一一映射关系
    protected WindowState mWin;
​
    private final Rect mTmpRect = new Rect();
    private final InsetsStateController mStateController;
    private final InsetsSourceControl mFakeControl;
    private @Nullable InsetsSourceControl mControl;
    private @Nullable InsetsControlTarget mControlTarget;
    private @Nullable InsetsControlTarget mPendingControlTarget;
    private @Nullable InsetsControlTarget mFakeControlTarget;
​
    private @Nullable ControlAdapter mAdapter;
    private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
    private TriConsumer<DisplayFrames, WindowState, Rect> mImeFrameProvider;
    private final Rect mImeOverrideFrame = new Rect();
    private boolean mIsLeashReadyForDispatching;
​
    /** The visibility override from the current controlling window. */
    private boolean mClientVisible;
​
    /**
     * Whether the window is available and considered visible as in {@link WindowState#isVisible}.
     */
    private boolean mServerVisible;
​
    private boolean mSeamlessRotating;
    private long mFinishSeamlessRotateFrameNumber = -1;
​
    private final boolean mControllable;
}
InsetsSourceConsumer

对单一 InsetsSource 的消费者,其内部持有 InsetsSourceControl,可以控制其leash的可见性和动画;

public class InsetsSourceConsumer {
​
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(value = {ShowResult.SHOW_IMMEDIATELY, ShowResult.IME_SHOW_DELAYED, ShowResult.IME_SHOW_FAILED})
    @interface ShowResult {
        /**
         * Window type is ready to be shown, will be shown immidiately.
         */
        int SHOW_IMMEDIATELY = 0;
        /**
         * Result will be delayed. Window needs to be prepared or request is not from controller.
         * Request will be delegated to controller and may or may not be shown.
         */
        int IME_SHOW_DELAYED = 1;
        /**
         * Window will not be shown because one of the conditions couldn't be met.
         * (e.g. in IME's case, when no editor is focused.)
         */
        int IME_SHOW_FAILED = 2;
    }
​
    // 所属的InsetController
    protected final InsetsController mController;
    // 单一Insets的可见性
    protected boolean mRequestedVisible;
    // 本地state
    protected final InsetsState mState;
    // InsetsSource type
    protected final @InternalInsetsType int mType;
​
    private static final String TAG = "InsetsSourceConsumer";
    private final Supplier<Transaction> mTransactionSupplier;
    // 持有InsetsSourceControl变量可以实现对单一InsetsSource的控制
    private @Nullable InsetsSourceControl mSourceControl;
    private boolean mHasWindowFocus;
    private Rect mPendingFrame;
    private Rect mPendingVisibleFrame;
​
    /**
     * Indicates if we have the pending animation. When we have the control, we need to play the
     * animation if the requested visibility is different from the current state. But if we haven't
     * had a leash yet, we will set this flag, and play the animation once we get the leash.
     */
    private boolean mIsAnimationPending;
​
    public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
            Supplier<Transaction> transactionSupplier, InsetsController controller) {
        mType = type;
        mState = state;
        mTransactionSupplier = transactionSupplier;
        mController = controller;
        mRequestedVisible = getDefaultVisibility(type);
    }
}
InsetsState

保存系统中所有的Insets的状态,他是状态描述者,持有系统中可以产生Window Insets的window状态;

当一个 Window 在客户端被添加、更新的时候,ViewRootImpl 将会调用 relayoutWindow 方法,从 WMS 获取当前 Window 的 InsetsState 信息;

public class InsetsState implements Parcelable {
​
    public static final InsetsState EMPTY = new InsetsState();
​
    /**
     * Internal representation of inset source types. This is different from the public API in
     * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
     * at the same time.
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = "ITYPE", value = {
        ITYPE_STATUS_BAR,
        ITYPE_NAVIGATION_BAR,
        ITYPE_CAPTION_BAR,
        ITYPE_TOP_GESTURES,
        ITYPE_BOTTOM_GESTURES,
        ITYPE_LEFT_GESTURES,
        ITYPE_RIGHT_GESTURES,
        ITYPE_TOP_MANDATORY_GESTURES,
        ITYPE_BOTTOM_MANDATORY_GESTURES,
        ITYPE_LEFT_MANDATORY_GESTURES,
        ITYPE_RIGHT_MANDATORY_GESTURES,
        ITYPE_TOP_TAPPABLE_ELEMENT,
        ITYPE_BOTTOM_TAPPABLE_ELEMENT,
        ITYPE_LEFT_DISPLAY_CUTOUT,
        ITYPE_TOP_DISPLAY_CUTOUT,
        ITYPE_RIGHT_DISPLAY_CUTOUT,
        ITYPE_BOTTOM_DISPLAY_CUTOUT,
        ITYPE_IME,
        ITYPE_CLIMATE_BAR,
        ITYPE_EXTRA_NAVIGATION_BAR
    })
    public @interface InternalInsetsType {}
​
    // 通过mSources集合来维护所有的Window Insets Info
    // mSources变量维护所有产生Insets的window(也就是InsetsSource)的状态
    private InsetsSource[] mSources = new InsetsSource[SIZE];
​
    /**
     * The frame of the display these sources are relative to.
     */
    // 默认情况下是指整个显示屏幕,也就是屏幕的区域
    private final Rect mDisplayFrame = new Rect();
    
    public InsetsState() {
    }
​
    public InsetsState(InsetsState copy) {
        set(copy);
    }
​
    public InsetsState(InsetsState copy, boolean copySources) {
        set(copy, copySources);
    }
    ……………………
​
    public void set(InsetsState other) {
        set(other, false /* copySources */);
    }
​
    public void set(InsetsState other, boolean copySources) {
        mDisplayFrame.set(other.mDisplayFrame);
        if (copySources) {
            for (int i = 0; i < SIZE; i++) {
                InsetsSource source = other.mSources[i];
                mSources[i] = source != null ? new InsetsSource(source) : null;
            }
        } else {
            for (int i = 0; i < SIZE; i++) {
                mSources[i] = other.mSources[i];
            }
        }
    }
}

InsetsState的核心方法:

  • WindowInsets calculateInsets:基于当前InsetsSource设置计算新的WindowInsets;
  • void processSource:根据计算值更新Source值;
InsetsStateController

管理所有窗口的Insets的state;

class InsetsStateController {
    // 代表最后一次屏幕持有的所有类型的Insets状态信息
    private final InsetsState mLastState = new InsetsState();
    // 代表当前屏幕的所有类型的Insets状态信息
    private final InsetsState mState = new InsetsState();
    private final DisplayContent mDisplayContent;
​
    // 用于维护InsetSource生产者的集合,后续DisplayContent、InsetsPolicy都是通过InsetsStateController中的mProviders集合来获取对应类型的InsetsSourceProvider来获取对应类型的InsetsSource实例
    private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>();
    private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
            new ArrayMap<>();
    private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
​
    /** @see #onControlFakeTargetChanged */
    private final SparseArray<InsetsControlTarget> mTypeFakeControlTargetMap = new SparseArray<>();
​
    private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
}

InsetsControlTarget是一个接口,代表了可以控制Insets状态的对象,WindowState实现了该接口,WindowState是每个Window在WMs的状态描述,所以每一个Window都具有控制Insets的能力;

class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
        InsetsControlTarget {
InsetsController

它是WindowInsets在client端的实现,用来控制Insets ,InsetsController只在ViewRootImpl里面创建的,每个Window会对应一个ViewRootImpl,同样每个ViewRootImpl会对应每个InsetsController;

public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
    /** The local state */
    // 记录本地State (Client端的Insetsstate)
    private final InsetsState mState = new InsetsState();
​
    /** The state dispatched from server */
    // 从system端传来的InsetsState
    private final InsetsState mLastDispatchedState = new InsetsState();
​
    /** The state sent to server */
    // 发送给系统端的InsetsState
    private final InsetsState mRequestedState = new InsetsState();
​
    private final Rect mFrame = new Rect();
    private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
    // 持有InsetsSourceConsumer
    private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
    private final Host mHost;
    private final Handler mHandler;
​
    private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
    private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
    private final ArrayList<WindowInsetsAnimation> mTmpRunningAnims = new ArrayList<>();
    private final List<WindowInsetsAnimation> mUnmodifiableTmpRunningAnims =
            Collections.unmodifiableList(mTmpRunningAnims);
    private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
    private WindowInsets mLastInsets;
}

总结

WindowInsets架构.png

InsetsState作为InsetsSource的存储集合,维护着insets信息的状态,InsetsState在Client和Server端都分别持有一组;

  • Client InsetsState:在InsetsController中定义,用于存储本地的InsetsState,当本地InsetsState发生变更之后,会将其同步到mRequestedState的InsetsState中,这个InsetsState就是作为向Server发送变更后的InsetsState信息;
  • Server InsetsState:在WindowState中定义,每一个ViewRootImpl基本对应一个WindowState,这个InsetsState记录的是Client请求变更的InsetsState;

Insets类图.png

  • 每个ViewRootImpl对应一个InsetsController实例,他是一个App进程中控制Insets的核心类,用于保存传递系统中产生Insets的window的状态和动画需要的leash以及控制播放动画;
  • InsetsSource是对产生Insets的窗口的状态描述,包括可见性以及Insets的大小;
  • 每个InsetsController会持有一个成员变量mState(InsetsState),它保存了系统中所有产生Insets的Window(InsetsSource)的状态列表,状态主要是指可见性以及产生Insets的window的区域大小;
  • InsetsSourceConsumer 是用来消费特定InsetsSource,消费主要是指对产生Insets 的window即InsetsSource进行可见性控制以及播放动画,通过持有的window的Leash来实现,也就是mSourceControl(InsetsSourceControl);
  • 每个InsetsController会持有多个InsetsSourceConsumer,他持有一个InsetsSourceConsumers列表,SparseArray mSourceConsumers;