WCT系列(一):WindowContainerTransaction类详解

1、WindowContainerTransaction是什么:

windowContainerTransaction类的对象是用来存储对windowContainer的修改的一个集合,windowContainer。因为应用侧是无法直接操作windowContainer的,如果应用侧需要修改windowContainer的话,需要通过系统侧对windowContainer进行修改,这就涉及了信息的跨进程传输了。所以首先,WindowContainerTransaction类应该是一个磕跨进程传输的类。看到定义:
public final class WindowContainerTransaction implements Parcelable
也证实了这个猜测,其实现了Parcelable接口。然后就是另一个问题,应用通过系统侧修改WindowContainer的话,必定会告诉他修改窗口的什么属性,然后进去这个类找找代码:

 * Sets whether a container or its children should be hidden. When {@code false}, the existing
 * visibility of the container applies, but when {@code true} the container will be forced
 * to be hidden.
 */
@NonNull
public WindowContainerTransaction setHidden(
        @NonNull WindowContainerToken container, boolean hidden) {
    Change chg = getOrCreateChange(container.asBinder());
    chg.mHidden = hidden;
    chg.mChangeMask |= Change.CHANGE_HIDDEN;
    return this;
}

这里可以看到一个设置窗口及其子窗口是否被隐藏的接口,暂时不详细叙述这个方法的实现。这里可以看到修改这个hidden属性是通过一个Change类的对象实现的,Change类是WindowContainerTransaction的内部类,也是实现了Parcelable接口的。而且WindowContainerTransaction类内部也维护了一个包含Change类的Map:
private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
所以可以大概知道,对窗口的修改应该是通过Change类的对象去描述的。

所以目前可以得知WindowContainerTransaction类的作用,他的对象就是应用侧需要对窗口进行的修改的集合。而这个修改,则是通过Change类的对象描述的。
这里通过一个例子来展示下WindowContainerTransaction类的使用,该段代码位于TaskView.java中:

private void updateTaskVisibility() {
        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
        mSyncQueue.queue(wct);
        if (mListener == null) {
            return;
        }
        int taskId = mTaskInfo.taskId;
        mSyncQueue.runInSync((t) -> {
            mListenerExecutor.execute(() -> {
                mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
            });
        });
    }

由上述示例可见这个WindowContainerTransaction类的使用过程为:1.创建一个WindowContainerTransaction对象;2.通过该对象设置你需要修改的属性;3.将包含步骤2设置的修改的wct对象放入队列,runInsync方法就放到后面再讲。

2、如何通过WindowContainerTransaction修改窗口属性:

前面已经讲到了WindowContainerTransaction中的setHidden方法,那就继续分析这个方法:
public WindowContainerTransaction setHidden(
        @NonNull WindowContainerToken container, boolean hidden) {
    Change chg = getOrCreateChange(container.asBinder());
    chg.mHidden = hidden;
    chg.mChangeMask |= Change.CHANGE_HIDDEN;
    return this;
}

1)、看参数列表:

参数列表第一个是一个WindowContainerToken对象,看一下定义,这个类是什么含义:

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();
    }
    …………………………………
}

从代码上看,这个类也是实现了Parcelable,然后主要是包装了一个IWindowContainerToken类型的对象。因为系统侧的WMS里要管理所有的WindowContainer,那么WMS中该如何区分每一个WindowContainer呢?那就得用唯一的身份标识,即这个IWindowContainerToken,通过这个Token,WMS可以区分不同的WindowContainer,可以简单理解成,我们每个人其实在国家的户籍管理的制度里,就是用一个身份证号进行辨识的,在人口管理的系统中,我们是以身份证号进行区分的,所以身份证号就是这个Token。而WMS也可以通过WindowContainerToken的asBinder()方法访问到对应WindowContainer的方法。
而我们怎么获取到这个WindowContainerToken方法呢?从示例代码中可见:
wct.setWindowingMode(topRunningTask.token, WINDOWING_MODE_UNDEFINED);
可以通过TaskInfo类的token属性获取。看到TaskInfo类的token属性,正是WindowContainerToken类型的对象。那问题又来了,我们的这个token属性是在什么时候进行的赋值操作的呢?
这里得找到Task.java中的fillTaskInfo()方法了,这里可见info.token的赋值:

void fillTaskInfo(TaskInfo info, boolean stripExtras, @Nullable TaskDisplayArea tda) {
………………………………………………………………
    info.token = mRemoteToken.toWindowContainerToken();
………………………………………………………………
 }

当然,根据这个RemoteToken类型的实例化可以找到,只有DisplayArea、TaskFragment和Task类初始化时会初始化RemoteToken对象。
这里再提一个小小的疑问:mRemoteToken又是什么?看到Task类中没有定义,于是找到其父类中,最后在WindowContainer类中发现如下定义:
首先他实现了一个aidl接口的Stub类,所以其实RemoteToken就是一个binder的server端的抽象类Stub的子类的实现 。他包含了对WindowContainer的引用和WindowContainerToken。

static class RemoteToken extends IWindowContainerToken.Stub {

   final WeakReference<WindowContainer> mWeakRef;
    private WindowContainerToken mWindowContainerToken;

   RemoteToken(WindowContainer container) {
        mWeakRef = new WeakReference<>(container);
    }

   @Nullable
    WindowContainer getContainer() {
        return mWeakRef.get();
    }

   static RemoteToken fromBinder(IBinder binder) {
        return (RemoteToken) binder;
    }

   WindowContainerToken toWindowContainerToken() {
        if (mWindowContainerToken == null) {
            mWindowContainerToken = new WindowContainerToken(this);
        }
        return mWindowContainerToken;
    }

   @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(128);
        sb.append("RemoteToken{");
        sb.append(Integer.toHexString(System.identityHashCode(this)));
        sb.append(' ');
        sb.append(mWeakRef.get());
        sb.append('}');
        return sb.toString();
    }
}

而且在WindowContainerToken中的mRealToken其实就是IWindowContainerToken的对象,也就是说,mRealToken其实就是一个RemoteToken类型的对象,他才是能够通过弱引用真正的指向WindowContainer,同时还包含WindowContainerToken属性。这里再多说一个点,就是WindowContainerToken,只有Task和DisplayArea才会有(RemoteToken只有这两个类才会初始化,Task类和TaskFragment都算做Task)。

然后第二个参数就是需要设置的属性了,就不再多说;

2)、setHidden方法的实现:

public WindowContainerTransaction setHidden(
        @NonNull WindowContainerToken container, boolean hidden) {
    Change chg = getOrCreateChange(container.asBinder());
    chg.mHidden = hidden;
    chg.mChangeMask |= Change.CHANGE_HIDDEN;
    return this;
}

该方法第一步,定义一个Change对象chg,定义后通过getOrCreateChange方法对chg进行赋值。继续查看该方法的实现。

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

定义一个Change类型的对象out,然后在mChanges中去get一个Change,看下mChanges的定义发现,这是一个ArrayMap。Key是IBinder(这个IBinder的对象就是一个WindowContainerToken的对象,对应唯一的WindowContainer),value是Change。
private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
那就知道了,肯定有有地方会把这个键值对IBinder和Change组成的键值对放进mChanges中。这里如果是刚初始化的WindowContainerTransaction对象,mChanges肯定是空的,所以out == null,则会将token和out放进mChanges,并返回out。注意这里out只是一个默认的Change()方法构造出来的对象。

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;
    public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
    public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
    public static final int CHANGE_DRAG_RESIZING = 1 << 8;

   private final Configuration mConfiguration = new Configuration();
    private boolean mFocusable = true;
    private boolean mHidden = false;
    private boolean mIgnoreOrientationRequest = false;
    private boolean mForceTranslucent = false;
    private boolean mDragResizing = 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 Change() {}
…………………………………………………………………………………………………
}

所以Out的属性都是默认值,接下来就是对这个Change进行赋值了,我们看的是setHidden方法,所以需要修改Hidden相关的属性。首先是修改chg.mHidden属性,然后修改chg.mChangeMask。
mHidden属性就可以简单的认为是否需要隐藏,下面的mChangeMask就是一个标志位,通过这个属性能得知提交的WindowContainerTransaction进行了哪些属性的修改。
通过Change的初始值可知,mChangeMask = 0;是int类型。CHANGE_HIDDEN = 1<<3; 通过将二者进行异或运算,就能将标志着HIDDEN属性变化的第三位设置为1.
在这里插入图片描述

于是,在系统侧应用WindowContainerTransaction的时候,可以看到,这里就会通过mChangeMask属性与标志位的结果进行判断后,再对修改进行应用。

private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
    int effects = applyChanges(tr, c, null /* errorCallbackToken */);
    final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();

   if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
    if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
            effects = TRANSACT_EFFECTS_LIFECYCLE;
        }
    }

   if ((c.getChangeMask()
            & WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT) != 0) {
        tr.setForceTranslucent(c.getForceTranslucent());
        effects = TRANSACT_EFFECTS_LIFECYCLE;
    }
…………………………………………………………………………………………
}

3)reparent方法的实现:

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;
}

这里不是通过Change保存reparent操作,而是通过mHierarchyOps来保存修改的。看下定义,mHierarchyOps 是一个ArrayList类型的对象。里面存储的是HierarchyOp类型的变量。

private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();

然后再看到HierarchyOp的构造方法,这里container就是需要重新设置parent的WindowContainer,然后reparent就是新的parent,toTop代表reparent操作后是否需要将子WindowContainer移动到父

WindowContainerToppublic static HierarchyOp createForReparent(
        @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
    return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
            .setContainer(container)
            .setReparentContainer(reparent)
            .setToTop(toTop)
            .build();
}

本文代码都是基于Android13。

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个错误提示说明在CentOS 7上执行脚本时发生了问题。错误指出了问题的源头是eclipse执行文件中的动态链接器(ld-linux.so.2),该动态链接器无法正确识别该文件。这种情况通常是由于文件损坏、格式不正确或与系统不兼容等原因导致的。 要解决这个问题,你可以尝试以下几个步骤: 1. 首先确认你下载的eclipse执行文件是否完整无损坏。可以重新下载eclipse,并确保文件完整,无错误。 2. 检查你的CentOS 7系统是否正确安装了所需的依赖库。使用以下命令检查缺失的库文件: ``` ldd eclipse ``` 如果缺少某些库文件,则尝试安装这些库文件,例如: ``` yum install libstdc++.so.6 ``` 这个命令将安装缺失的libstdc++库文件。 3. 确认你的eclipse执行文件是否为CentOS 7系统所支持的架构(32位或64位)。你可以使用以下命令检查系统架构: ``` uname -a ``` 然后确保下载的eclipse执行文件与系统架构相匹配。 4. 如果以上步骤都无法解决问题,可以尝试升级你的系统或者使用其他版本的eclipse。 总之,错误"./wct.sh: ./eclipse: /lib/ld-linux.so.2: bad elf interpreter"表明执行的eclipse文件无法识别其中的动态链接器。通过检查文件完整性、安装缺失的库文件、确认系统架构以及升级系统等方法,你可以尝试解决这个问题。 ### 回答2: 这个错误提示出现是因为在运行脚本时,CentOS 7系统找不到正确的ELF解释器。ELF(Executable and Linkable Format)是一种可执行文件的格式,而解释器则用于执行可执行文件。 通常情况下,CentOS 7系统使用的是位于/lib64/ld-linux-x86-64.so.2的64位解释器,然而在你的情况下,系统在指定的路径下找不到该解释器,导致出现错误。 解决这个问题的方法有两种: 1. 安装32位的解释器:这种方法适用于你要运行的可执行文件是32位的。可以通过运行以下命令来安装32位解释器:sudo yum install glibc.i686。这样就会安装一个32位版本的glibc(GNU C标准库),其中包含了所需的解释器。 2. 更换正确的解释器路径:这种方法适用于你有可执行文件的正确版本(64位或32位),但在错误的解释器路径下。可以通过编辑脚本文件,将解释器路径更改为正确的路径。你可以使用命令which ld-linux来找到正确的解释器路径,然后将其替换脚本中的错误路径。 无论你选择哪种方法,请记得在安装或更改任何系统文件之前备份重要数据。此外,确保你的系统是最新的,并且已经安装了所有必要的软件包和依赖关系。 希望以上解答对你有所帮助! ### 回答3: 这个错误信息指的是在运行CentOS7操作系统中的一个叫做"wct.sh"的脚本时出现了问题。具体错误是脚本中的"eclipse"这个可执行文件无法正常运行,因为它依赖的"/lib/ld-linux.so.2"这个动态链接库(Dynamic Link Library,简称DLL)存在问题。 这个错误通常发生在以下三种情况下: 1. "eclipse"可执行文件本身损坏或无效; 2. 对应的动态链接库"/lib/ld-linux.so.2"损坏或无效; 3. 系统环境中缺少该版本的动态链接库。 解决这个问题可以尝试以下几个步骤: 1. 检查并确保"./wct.sh"脚本文件和"eclipse"可执行文件的完整性和有效性。可以尝试重新下载或替换它们。 2. 如果"/lib/ld-linux.so.2"动态链接库损坏或无效,可以尝试重新安装或更新它。使用以下命令进行检查和安装更新: ``` yum provides ld-linux.so.2 yum install glibc ``` 这将更新glibc(GNU C Library)包,其中包含了这个动态链接库。 3. 如果系统中缺少该版本的动态链接库,可以尝试从其他可靠的来源安装或更新该包。可以搜索"glibc"或"/lib/ld-linux.so.2"来获取相关信息和指导。 如果以上步骤仍无法解决问题,可能需要检查系统的相关配置和其他依赖项。根据具体情况,可能需要寻求专业人士的帮助来解决该问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值