重学wms:你知道什么是AdjacentTask吗?分屏模式的判断方法

背景:

在我们做多窗口开发过程中一般有以下几种模式:

    public static final int WINDOWING_MODE_FULLSCREEN = 1;
    /** Always on-top (always visible). of other siblings in its parent container. */
    public static final int WINDOWING_MODE_PINNED = 2;
    /** Can be freely resized within its parent container. */
    // TODO: Remove once freeform is migrated to wm-shell.
    public static final int WINDOWING_MODE_FREEFORM = 5;
    /** Generic multi-window with no presentation attribution from the window manager. */
    public static final int WINDOWING_MODE_MULTI_WINDOW = 6;

明显自由窗口和pip模式可以在系统中直接通过windowingMode的值进行直接判断,这时候大家是否想过我们分屏模式应该如何判断啊?
可能很多同学会有如下回答:
直接使用windowingMode判断,看看是否为WINDOWING_MODE_MULTI_WINDOW不就行了。
但是大家注意哈,正常情况下确实一般分屏模式的两个task是mode为multi-window,但是大家注意mutilwindow却不一定为分屏模式哈,比如以前的车载桌面那个Activity嵌入显示的模式,也是multiwindow但不是分屏模式。
那么有啥好办法可以判断当前显示的Activity或者task是否为分屏呢?下面本文就带大家来剖析一下该问题。

系统如何判断分屏模式?

在系统代码中收到如下代码来判断当前显示的Task是否处于分屏模式

final boolean inSplitScreenMode  =
                defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
                        && task.getTopLeafTask().getAdjacentTask() != null)
                        != null;

针对上面代码展开源码分析有如下几个过程:

1、遍历defaultTaskDisplayArea这个节点下面的所有RootTask

2、接下来开始挨个RootTask进行,判断条件
a. task是显示状态isVisible为true
b. 判断task的最顶部的叶子task
c. 判断叶子task的AdjacentTask是否不为null
如果不为null说明就是分屏模式。

上面判断代码让大家最疑惑的应该是为啥可以通过getAdjacentTask来判断是否为分屏模式呢?

源码分析adjacent相关设置

上面判断为分屏模式代码中,关键就是靠
这里的AdjacentTask属于熟悉而又陌生的方法,熟悉在于你经常见到它在framework代码中,陌生在于你可能没有本质理解它什么意思,想不到它的实际场景。
Adjacent 翻译后的中文意思就是“ 相邻;邻近的 ”,也就是mAdjacentTaskFragment本质上代表是一个邻居Task。

   /** The TaskFragment that is adjacent to this one. */
    @Nullable
    private TaskFragment mAdjacentTaskFragment;

那么这个邻居Task字面意思理解就是两个Task在一起,相邻着的意思。

那么我们关键就看看mAdjacentTaskFragment到底是在哪里进行的赋值,通过代码调试追查发现如下地方进行的设置。
在这里插入图片描述

分屏设置AdjacentTask的调用堆栈:

setAdjacentTaskFragment:447, TaskFragment (com.android.server.wm)
setAdjacentRootsHierarchyOp:2015, WindowOrganizerController (com.android.server.wm)
applyHierarchyOp:1083, WindowOrganizerController (com.android.server.wm)
applyTransaction:718, WindowOrganizerController (com.android.server.wm)
applyTransaction:579, WindowOrganizerController (com.android.server.wm)
applyTransaction:596, WindowOrganizerController (com.android.server.wm)
lambda$applySyncTransaction$0:259, WindowOrganizerController (com.android.server.wm)
accept:0, WindowOrganizerController$$ExternalSyntheticLambda1 (com.android.server.wm)
startLegacySyncOrQueue:1481, TransitionController (com.android.server.wm)
applySyncTransaction:258, WindowOrganizerController (com.android.server.wm)
onTransact:204, WindowOrganizerController (com.android.server.wm)
execTransactInternal:1505, Binder (android.os)
execTransact:1444, Binder (android.os)

明显可以看到是在systemui进程创建分屏的两个task时候会触发这个setAdjacentTaskFragment设置。
这里来详细看看setAdjacentTaskFragment方法:

    void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
        if (mAdjacentTaskFragment == taskFragment) {
            return;
        }
        resetAdjacentTaskFragment();
        if (taskFragment != null) {
            mAdjacentTaskFragment = taskFragment;
            //这里把当前task设置成参数task的AdjacentTask
            taskFragment.setAdjacentTaskFragment(this);
        }
    }

简单说就是Task1和Task2分别设置对方成为自己的AdjacentTask,形成如下图的依赖模式
在这里插入图片描述

systemui如何触发到system端setAdjacentTaskFragment

frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java

StageCoordinator的onRootTaskAppeared方法中触发的设置

在这里插入图片描述其实这里的也很,注释明显

// Make the stages adjacent to each other so they occlude what's behind them.

保证上下分屏的task与彼此相邻。

frameworks/base/core/java/android/window/WindowContainerTransaction.java

 /**
     * Sets to containers adjacent to each other. Containers below two visible adjacent roots will
     * be made invisible. This currently only applies to TaskFragment containers created by
     * organizer.
     * @param root1 the first root.
     * @param root2 the second root.
     */
    @NonNull
    public WindowContainerTransaction setAdjacentRoots(
            @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
        mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
                root1.asBinder(),
                root2.asBinder()));
        return this;
    }

这里主要就是createForAdjacentRoots方法,创建对一个HierarchyOp对象,然后跨进程传递system_server进行task的setAdjacentTaskFragment操作

   /** Create a hierarchy op for setting adjacent root tasks. */
        public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
                    .setContainer(root1)
                    .setReparentContainer(root2)
                    .build();
        }
<think>好的,用户遇到了Git合并时本地更改会被覆盖的问题,需要解决方案。根据之前的引用资料,解决方法主要有commit、stash和revert三种。首先,我得确认用户的具体情况,比如他们的本地更改是否已经完成,是否需要保留,或者是否可以丢弃。用户可能不太清楚这三种方法的适用场景,需要详细解释每个选项的步骤和优缺点。比如,commit适用于需要保留更改的情况,stash适合临时保存以便后续恢复,而revert则是直接撤销更改,适用于不需要保留的情况。需要结构清晰,分点说明,每个方法都给出操作步骤和适用场景,帮助用户理解哪种情况最适合他们。还要注意使用中文,避免技术术语过于复杂,让用户容易理解。同时,生成相关问题,可能涉及如何恢复stash的内容,或者如何避免频繁冲突等,扩展用户的知识点。另外,要确保LaTeX格式正确,但在这个问题中可能用不上数学公式,所以主要关注代码块和引用标识。检查引用是否正确,比如引用[^1]和[^2]的来源是否相关,用户提供的引用内容是否准确对应解决方案。 最后,确保回答真实可靠,操作步骤准确无误,避免误导用户。可能还需要提醒用户根据具体情况选择方法,比如如果有未完成的实验性代码,stash可能是更好的选择,而如果是确定要保留的修改,则应该commit。</think>要解决Git合并时本地更改被覆盖的问题,可根据不同场景选择以下三种方法: ### 一、提交本地更改(commit) **适用场景**:当本地更改已测试通过且需要保留到版本历史中。 **操作步骤**: 1. 将改动添加到暂存区: ```bash git add . # 添加所有文件 # 或指定文件 git add file1.txt file2.py ``` 2. 提交更改并添加描述: ```bash git commit -m "完成用户登录模块优化" ``` 3. 执行合并操作: ```bash git merge origin/main ``` **优点**:保留完整的开发记录,便于后续追溯[^1]。 ### 二、暂存更改(stash) **适用场景**:需要临时保存未完成的修改(如调试代码),优先处理合并操作。 **操作步骤**: 1. 保存当前工作状态: ```bash git stash # 可选添加备注 git stash save "用户登录模块调试代码" ``` 2. 查看暂存记录列表: ```bash git stash list ``` 3. 合并远程分支: ```bash git merge origin/main ``` 4. 恢复暂存内容: ```bash git stash pop # 恢复并删除最近一次暂存 # 或指定特定暂存 git stash apply stash@{1} ``` **优点**:保持工作目录整洁,特别适合处理紧急合并任务[^2]。 ### 三、撤销本地更改(revert) **适用场景**:本地修改可丢弃(如实验性代码或错误修改)。 **操作步骤**: 1. 撤销所有未提交的修改: ```bash git checkout -- . # 全部文件 # 或指定文件 git checkout -- file1.txt ``` 2. 执行合并操作: ```bash git merge origin/main ``` **风险提示**:此操作不可逆,需确认文件无需保留。 --- ### 对比决策表 | 方法 | 保留记录 | 可恢复性 | 适用阶段 | |---------|----------|----------|------------------| | commit | ✅ | ✅ | 功能开发完成时 | | stash | ❌ | ✅ | 开发中途临时切换 | | revert | ❌ | ❌ | 放弃无效修改 | ### 扩展应用场景 - 当存在多个暂存时,可用`git stash list`查看记录,通过`git stash apply stash@{n}`精准恢复 - 合并后出现冲突时,可使用`git mergetool`可视化工具解决冲突 - 长期分支开发推荐使用`git rebase`保持提交历史线性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值