一、定义
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.
*/
@NonNull
public 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 state
if (minimizedChanged) {
mMinimized = minimized;
}
// Always set this because we could be entering split when mMinimized is already true
wct.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.
*/
@NonNull
public 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.Config
public int getConfigSetMask() {
return mConfigSetMask;
}
@WindowConfiguration.WindowConfig
public 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.
*/
@NonNull
public 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-dependent
private 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类中。
——————————
欢迎大家一键三连支持,若需要更多资料,可直接点击文末CSDN官方认证微信卡片免费领取【保证100%免费】,也可在评论区扣1或私信↓↓↓
今天的文章wctha_android是什么软件分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/87172.html