1【Android 12】【WCT的定义】WindowContainerTransaction

编程入门 行业动态 更新时间:2024-10-13 02:21:31

1【Android 12】【WCT的<a href=https://www.elefans.com/category/jswz/34/1771289.html style=定义】WindowContainerTransaction"/>

1【Android 12】【WCT的定义】WindowContainerTransaction

一、定义

/*** Represents a collection of operations on some WindowContainers that should be applied all at* once.** @hide*/
@TestApi
public final class WindowContainerTransaction implements Parcelable {......
}

WindowContainerTransaction表示一些WindowContainer上应该一次性应用的操作集合。

从使用意义上来看,WindowContainerTransaction类和Transaction类比较相似,Transaction是应用在SurfaceControl上的操作集合,WindowContainerTransaction是应用在WindowContainer上的操作集合。另外WindowContainerTransaction也实现了Parcelable,这为其在系统服务端和App端之间的传输提供了支持。

二、使用

结合分屏的一处逻辑,看下WindowContainerTransaction是如何使用的,以下是进入分屏的过程中LegacySplitScreenController#splitPrimaryTask做的事情:

    public boolean splitPrimaryTask() {......final WindowContainerTransaction wct = new WindowContainerTransaction();// Clear out current windowing mode before reparenting to split task.wct.setWindowingMode(topRunningTask.token, WINDOWING_MODE_UNDEFINED);wct.reparent(topRunningTask.token, mSplits.mPrimary.token, true /* onTop */);mWindowManagerProxy.applySyncTransaction(wct);return true;}

1)、创建一个WindowContainerTransaction对象。

2)、调用WindowContainerTransaction#setWindowingMode。

3)、调用WindowContainerTransaction#reparent。

4)、调用WindowManagerProxy#applySyncTransaction发送当前WindowContainerTransaction,关于WCT的发送留在以后分析,这里重点分析第2、3点。

1 WindowContainerTransaction#setWindowingMode

    /*** Sets the windowing mode of the given container.*/@NonNullpublic WindowContainerTransaction setWindowingMode(@NonNull WindowContainerToken container, int windowingMode) {Change chg = getOrCreateChange(container.asBinder());chg.mWindowingMode = windowingMode;return this;}

内容很简单:

1)、根据传入的WindowContainerToken对象,通过WindowContainerTransaction#getOrCreateChange方法获取一个Change对象。

2)、将Change的成员变量mWindowingMode赋值为传入的windowingMode。

在分析WindowContainerTransaction#getOrCreateChange方法之前,先看下WindowContainerToken的作用:

/*** Interface for a window container to communicate with the window manager. This also acts as a* token.* @hide*/
@TestApi
public final class WindowContainerToken implements Parcelable {private final IWindowContainerToken mRealToken;/** @hide */public WindowContainerToken(IWindowContainerToken realToken) {mRealToken = realToken;}private WindowContainerToken(Parcel in) {mRealToken = IWindowContainerToken.Stub.asInterface(in.readStrongBinder());}/** @hide */public IBinder asBinder() {return mRealToken.asBinder();}......
}

WindowContainterToken实现了Parcelable,这为其跨进程传输提供了支持。WindowContainterToken内部有一个成员变量mRealToken,是一个IWindowContainerToken类型的token,在系统服务创建WindowContainer时候生成(目前只有DisplayArea和Task),对于WindowContainer来说是一个独特的跨进程的标识。WindowContainerToken可以看做是IBinder类型的token的封装,这个token通过WindowContainerToken#asBinder返回。

Task可以通过Task#fillTaskInfo方法将该Task对应的WindowContainerToken保存在对应的TaskInfo中,这样App进程可以先获取TaskInfo进而拿到这个token。

App端如果想通过WindowContainerTransaction的方法修改某个WindowContainer的属性,必须传入该WindowContainer对应的token,这样系统服务端在接收到这个WindowContainerTransaction的时候,才可以通过token知道需要修改哪些WindowContainer。

接着看下WindowContainerTransaction#getOrCreateChange的内容:

    private Change getOrCreateChange(IBinder token) {Change out = mChanges.get(token);if (out == null) {out = new Change();mChanges.put(token, out);}return out;}

从mChanges中查找该WindowContainerToken有没有相应的Change对象,没有就创建一个,然后以键值对的方式把这个WindowContainerToken对象和为其创建的Change对象加入到mChanges中。

mChanges是一个ArrayMap类型的WindowContainerTransaction的成员变量,以IBinder类型的WindowContainerToken对象为key,以WindowContainerTransaction为该WindowContainerToken创建的Change对象为value:

private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();

后续该WindowContainerTransaction发送到WM端的时候,WindowOrganizerController遍历这个WindowContainerTransaction的mChanges成员变量,对于mChanges中的每一个Change对象:

1)、从该Change中提取出WindowContainerToken,将该WindowContainerToken转化为WM端对应的WindowContainer对象。

2)、提取出Change中为该WindowContainerToken保存的WindowingMode,然后应用到第1步中转化得到的WindowContainer。

上面的第2步可以看WindowOrganizerController#applyChanges方法进一步了解一下:

    private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {.......final int windowingMode = change.getWindowingMode();......if (windowingMode > -1) {......container.setWindowingMode(windowingMode);}return effects;}

看到这里似乎对WindowContainerTransaction的运作方式有了一部分的了解了,App端如果想要通过WindowContainerTransaction修改WM端的WindowContainter的属性,需要这几步操作:

1)、App端首先要能够拿到这个WindowContainer对应的WindowContainerToken。

2)、创建一个WindowContainerTransaction对象,调用WindowContainerTransaction的相关方法对这个WindowContainerToken进行设置。

3)、发送WindowContainerTransaction。

2 WindowContainerTransaction#setFocusable

为了印证这个猜想,再看另外一个类似的方法WindowContainerTransaction#setFocusable。调用的地方在分屏相关逻辑处:

    public void setHomeMinimized(final boolean minimized) {......WindowContainerTransaction wct = new WindowContainerTransaction();final boolean minimizedChanged = mMinimized != minimized;// Update minimized stateif (minimizedChanged) {mMinimized = minimized;}// Always set this because we could be entering split when mMinimized is already truewct.setFocusable(mSplits.mPrimary.token, !mMinimized);......}

先创建一个WindowContainerTransaction,然后调用WindowContainerTransaction.setFocusable方法设置:

    /*** Sets whether a container or any of its children can be focusable. When {@code false}, no* child can be focused; however, when {@code true}, it is still possible for children to be* non-focusable due to WM policy.*/@NonNullpublic WindowContainerTransaction setFocusable(@NonNull WindowContainerToken container, boolean focusable) {Change chg = getOrCreateChange(container.asBinder());chg.mFocusable = focusable;chg.mChangeMask |= Change.CHANGE_FOCUSABLE;return this;}

该方法用来设置一个WindowContainer和其子WindowContainer是否可以获取焦点,同样是两步操作:

1)、根据传入的WindowContainerToken对象,通过WindowContainerTransaction#getOrCreateChange方法获取一个Change对象。

2)、将Change的成员变量mFocusable赋值为传入的focusable参数。

Change.mChange应用的地方依然是WindowOrganizerController#applyChanges:

    private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {......if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {if (container.setFocusable(change.getFocusable())) {effects |= TRANSACT_EFFECTS_LIFECYCLE;}}......}

套路都是一样的:

1)、从该Change中提取出WindowContainerToken,将该WindowContainerToken转化为WM端对应的WindowContainer对象。

2)、提取出Change中为该WindowContainerToken保存的focusable属性,然后应用到第1步中转化得到的WindowContainer。

3 WindowContainerTransaction.Change类

    /*** Holds changes on a single WindowContainer including Configuration changes.* @hide*/public static class Change implements Parcelable {public static final int CHANGE_FOCUSABLE = 1;public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;public static final int CHANGE_PIP_CALLBACK = 1 << 2;public static final int CHANGE_HIDDEN = 1 << 3;public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;private final Configuration mConfiguration = new Configuration();private boolean mFocusable = true;private boolean mHidden = false;private boolean mIgnoreOrientationRequest = false;private int mChangeMask = 0;private @ActivityInfo.Config int mConfigSetMask = 0;private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;private Rect mPinnedBounds = null;private SurfaceControl.Transaction mBoundsChangeTransaction = null;private Rect mBoundsChangeSurfaceBounds = null;private int mActivityWindowingMode = -1;private int mWindowingMode = -1;......public int getWindowingMode() {return mWindowingMode;}public int getActivityWindowingMode() {return mActivityWindowingMode;}public Configuration getConfiguration() {return mConfiguration;}/** Gets the requested focusable state */public boolean getFocusable() {if ((mChangeMask & CHANGE_FOCUSABLE) == 0) {throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first");}return mFocusable;}/** Gets the requested hidden state */public boolean getHidden() {if ((mChangeMask & CHANGE_HIDDEN) == 0) {throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first");}return mHidden;}/** Gets the requested state of whether to ignore orientation request. */public boolean getIgnoreOrientationRequest() {if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) {throw new RuntimeException("IgnoreOrientationRequest not set. "+ "Check CHANGE_IGNORE_ORIENTATION_REQUEST first");}return mIgnoreOrientationRequest;}public int getChangeMask() {return mChangeMask;}@ActivityInfo.Configpublic int getConfigSetMask() {return mConfigSetMask;}@WindowConfiguration.WindowConfigpublic int getWindowSetMask() {return mWindowSetMask;}/*** Returns the bounds to be used for scheduling the enter pip callback* or null if no callback is to be scheduled.*/public Rect getEnterPipBounds() {return mPinnedBounds;}public SurfaceControl.Transaction getBoundsChangeTransaction() {return mBoundsChangeTransaction;}public Rect getBoundsChangeSurfaceBounds() {return mBoundsChangeSurfaceBounds; }......}

持有对单一WindowContainer的包括Configuration的修改。

通过对WindowContainerTransaction#setWindowingMode和WindowContainerTransaction#setFocusable这两部分的分析,可以看到不管是改变WindowContainer的windowingMode,或是focusable属性,都需要:

1)、创建一个WindowContainerTransaction对象,调用WindowContainerTransaction提供的方法对WindowContainerToken进行设置,实际上就是将客户端对WindowContainer期望的一些修改先保存到WindowContainerTransaction为WindowContainerToken创建的Change对象的相关成员变量中。

2)、发送创建的WindowContainerTransaction到WM端,WM端获取到WindowContainerTransaction.Change相关成员变量的值,然后应用到WindowContainer上。

那么也就是说,WindowContainerTransaction.Change有多少成员变量,WindowContainerTransaction就可以向客户端提供多少可以改变WindowContainer的方法接口。

看下WindowContainerTransaction.Change的成员变量都有哪些:

        private final Configuration mConfiguration = new Configuration();private boolean mFocusable = true;private boolean mHidden = false;private boolean mIgnoreOrientationRequest = false;......private Rect mPinnedBounds = null;private SurfaceControl.Transaction mBoundsChangeTransaction = null;private Rect mBoundsChangeSurfaceBounds = null;private int mActivityWindowingMode = -1;private int mWindowingMode = -1;

重点看一下成员变量mConfiguration,毕竟Configuration中包含了很多的配置属性,但是WindowContainerTransaction并不支持对Configuration中所有的属性进行修改,主要是screenSize、windowingMode和bounds等。

目前来看设计WindowContainerTransaction是为多窗口功能服务的,因此WindowContainerTransaction.Change提供的这些方法已经满足需要了。之前都是SystemUI直接跨Binder调用系统服务的一些resizeStack之类的方法去修改分屏Task的bounds。有了WindowContainerTransaction.Chang之后,就可以把系统服务向客户端提供的对WindowContainer的所有修改方法统一组织起来,方便管理以及后续扩展新内容。

4 WindowContainerTransaction#reparent

我们上面只分析了LegacySplitScreenController#splitPrimaryTask中的一个方法WindowContainerTransaction#setWindowingMode,还有一个方法WindowContainerTransaction#reparent没有分析。

    /*** Reparents a container into another one. The effect of a {@code null} parent can vary. For* example, reparenting a stack to {@code null} will reparent it to its display.** @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to*              the bottom.*/@NonNullpublic WindowContainerTransaction reparent(@NonNull WindowContainerToken child,@Nullable WindowContainerToken parent, boolean onTop) {mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),parent == null ? null : parent.asBinder(),onTop));return this;}

该方法的作用是将一个WindowContainer重新reparent到另外一个WindowContainer,最终结果受到参数parent的影响,如果parent为null,那么将参数child子WindowContainer容器reparent到它对应的display上。

看下createForReparent方法:

        public static HierarchyOp createForReparent(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT,container, reparent, null, null, toTop, null);}

再看下mHierarchyOps:

    // Flat list because re-order operations are order-dependentprivate final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();

因此这个方法的内容是:

1)、根据传入的IBinder类型container和reparent创建一个HierarchyOp对象。

2)、将这个HierarchyOp对象添加到WindowContainerTransaction的成员变量mHierarchyOps中。

后续这个WindowContainerTransaction发送到系统服务端的时候,由WindowOrganizerController负责将以上两个IBinder类型的token转化为WindowContainer对象,然后进行reparent操作。

对比一下WindowContainerTransaction.Change的相关内容:

1)、WindowContainerTransaction创建的Change对象是和WindowContainerToken内部的token对象一一对应的,创建的时候会判断WindowContainerTransaction的mChanges中是否已经有了一个与token对应的Change对象,对应的是

2)、HierarchyOp对象在每次需要进行reparent的时候就会创建一次,对应的是一次层次结构上的操作。

5 WindowContainerTransaction.HierarchyOp类

在做出进一步的总结前,还是需要看下WindowContainerTransaction.HierarchyOp类的作用。

        private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent,int[] windowingModes, int[] activityTypes, boolean toTop,@Nullable Bundle launchOptions) {mType = type;mContainer = container;mReparent = reparent;mWindowingModes = windowingModes != null ?Arrays.copyOf(windowingModes, windowingModes.length) : null;mActivityTypes = activityTypes != null ?Arrays.copyOf(activityTypes, activityTypes.length) : null;mToTop = toTop;mLaunchOptions = launchOptions;}

创建HierarchyOp对象的时候会对其成员变量进行赋值,比较重要的是其中三个成员变量:

        // Container we are performing the operation on.private final IBinder mContainer;// If this is same as mContainer, then only change position, don't reparent.private final IBinder mReparent;// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.private final boolean mToTop;

mContainer是子WindowContainer对应的token,mReparent是父WindowContainer对应的token,mToTop表示reparent操作后是否需要将子WindowContainer移动到父WindowContainer的top。

这样来看,WindowContainerTransaction.HierarchyOp的作用逻辑和WindowContainerTransaction.Change类似。WindowContainerTransaction.HierarchyOp将App端设置的一次WindowContainer层级调整操作中的子WindowContainer对应的token和父WindowContainer对应的token保存起来,后续WindowContainerTransaction发送到服务端后,服务端读取HierarchyOp中token并转化为对应的WindowContainer对象,再完成此次WindowContainer层级调整。

根据mReparent的值,会有几种不同的情况:

1)、mReparent不为空,且不等于mContainer,这个是最普遍的reparent的操作,将子WindowContainer移入父WindowContainer中。

2)、mReparent不为空,且等于mContainer,那么此次操作不是reparent,而是reorder,根据mToTop的值调整mContainer在当前父容器中的位置。

3)、mReparent为空,那么将mContainer移入当前display中。

具体的代码情况在WCT的应用一文中详细分析。

三、总结

WindowContainerTransaction向客户端提供了远程修改系统服务中的WindowContainer的能力,类似于Transaction修改SurfaceControl,直白点说就是,由于客户端无法直接修改WindowContainer,所以客户端需要先告诉WindowContainerTransaction我想修改哪些WindowContainer的哪些内容,WindowContainerTransaction把这些期望的修改保存起来,后续客户端把这个WindowContainerTransaction发送到服务端,服务端读取WindowContainerTransaction中的设置后由服务端帮助客户端完成修改。

一般流程是:

1)、客户端创建一个WindowContainerTransaction对象。

2)、调用WindowContainerTransaction的相关方法,这一步需要将期望修改的WindowContainer对应的WindowContaienrToken对象作为参数传入。

3)、通过WindowOrganizer将WindowContainerTransaction发送到服务端,最终服务端读取WindowContainerTransaction中保存的参数完成相应操作。

WindowContainerTransaction支持以下两类修改:

1)、修改WindowContainer的属性,包括WindowingMode和Configuration之类,这类修改保存在WindowContainerTransaction.Change类中。

2)、修改WindowContainer的层级,既可以将一个子容器从当前父容器移入另外一个新的父容器中,也可以仅仅调整子容器在当前父容器中的位置,这类修改保存在WindowContainerTransaction.HierarchyOp类中。

更多推荐

1【Android 12】【WCT的定义】WindowContainerTransaction

本文发布于:2024-03-07 23:33:57,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1719226.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:定义   Android   WindowContainerTransaction   WCT

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!