Android SytemWindow,Z轴排序问题

近期工作中遇到一个问题,就是在System级别的Window排序中遇到了一些问题,事后分析原因可能是使用方式不太符合Android的设计原则。

不过在这个过程中也学习了很多东西,记录一下这过程中学习到的知识和最后的解决方案。

WindowState

Window的状态信息,其中的mBaseLayermSubLayer就是很多文章中的Z轴排序的 基础依据。mAttrs就是我们传入的window属性。System级别Window通过WindowManager直接addView添加的就不存在AppWindowToken

class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
    ...
    final WindowId mWindowId;
    WindowToken mToken;
    // The same object as mToken if this is an app window and null for non-app windows.
    AppWindowToken mAppToken;

    // mAttrs.flags is tested in animation without being locked. If the bits tested are ever
    // modified they will need to be locked.
    final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
    final DeathRecipient mDeathRecipient;
    private boolean mIsChildWindow;
    final int mBaseLayer;
    final int mSubLayer;
    ...
    WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
            PowerManagerWrapper powerManagerWrapper) {
1
        ...
    }
}

WindowToken

Window的令牌信息,管理窗口的句柄类。管理子窗口的主要类。

class WindowToken extends WindowContainer<WindowState> {
...
    private final Comparator<WindowState> mWindowComparator =
            (WindowState newWindow, WindowState existingWindow) -> {
        final WindowToken token = WindowToken.this;
        if (newWindow.mToken != token) {
            throw new IllegalArgumentException("newWindow=" + newWindow
                    + " is not a child of token=" + token);
        }

        if (existingWindow.mToken != token) {
            throw new IllegalArgumentException("existingWindow=" + existingWindow
                    + " is not a child of token=" + token);
        }

        return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
    };

    WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
            DisplayContent dc, boolean ownerCanManageAppTokens) {
        this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
                false /* roundedCornersOverlay */);
    }

    WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
            DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
        super(service);
        token = _token;
        windowType = type;
        mPersistOnEmpty = persistOnEmpty;
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;
        mRoundedCornerOverlay = roundedCornerOverlay;
        onDisplayChanged(dc);
    }
    ...
}

DisplayContent

DisplayContent继承于WindowContainer。这里就是解决问题的主要地方。这里对应的是屏幕,要注意的这里可能会有多个屏幕。而system级别的Window要看最终在那个WindowsContainers

class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
    ...
    /** Unique identifier of this stack. */
    private final int mDisplayId;

    /** The containers below are the only child containers the display can have. */
    // Contains all window containers that are related to apps (Activities)
    private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mService);
    // Contains all non-app window containers that should be displayed above the app containers
    // (e.g. Status bar)
    private final AboveAppWindowContainers mAboveAppWindowsContainers =
            new AboveAppWindowContainers("mAboveAppWindowsContainers", mService);
    // Contains all non-app window containers that should be displayed below the app containers
    // (e.g. Wallpaper).
    private final NonAppWindowContainers mBelowAppWindowsContainers =
            new NonAppWindowContainers("mBelowAppWindowsContainers", mService);
    // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
    // on the IME target. We mainly have this container grouping so we can keep track of all the IME
    // window containers together and move them in-sync if/when needed. We use a subclass of
    // WindowContainer which is omitted from screen magnification, as the IME is never magnified.
    private final NonMagnifiableWindowContainers mImeWindowsContainers =
            new NonMagnifiableWindowContainers("mImeWindowsContainers", mService);
    ...
    
    
}

最终问题解决思路就是增加自研的System级别的Window Type级别。assignChildLayers函数将mChildren中的WindowToken进行最后一个排序,也可以在mChildren的add位置进行一个排序。如果有特殊的需求,个人思路是不影响系统原本的流程,可以在合适的位置进行特殊的逻辑排序。

private final class AboveAppWindowContainers extends NonAppWindowContainers {
    AboveAppWindowContainers(String name, WindowManagerService service) {
        super(name, service);
    }

    @Override
    void assignChildLayers(SurfaceControl.Transaction t) {
        assignChildLayers(t, null /* imeContainer */);
    }

    void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) {
        boolean needAssignIme = imeContainer != null
                && imeContainer.getSurfaceControl() != null;
        for (int j = 0; j < mChildren.size(); ++j) {
            final WindowToken wt = mChildren.get(j);

            // See {@link mSplitScreenDividerAnchor}
            if (wt.windowType == TYPE_DOCK_DIVIDER) {
                wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1);
                continue;
            }
            wt.assignLayer(t, j);
            wt.assignChildLayers(t);

            int layer = mService.mPolicy.getWindowLayerFromTypeLw(
                    wt.windowType, wt.mOwnerCanManageAppTokens);

            if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw(
                            TYPE_INPUT_METHOD_DIALOG, true)) {
                imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
                needAssignIme = false;
            }
        }
        if (needAssignIme) {
            imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
        }
    }
}

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值