近期工作中遇到一个问题,就是在System
级别的Window
排序中遇到了一些问题,事后分析原因可能是使用方式不太符合Android
的设计原则。
不过在这个过程中也学习了很多东西,记录一下这过程中学习到的知识和最后的解决方案。
WindowState
Window
的状态信息,其中的mBaseLayer
和mSubLayer
就是很多文章中的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开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题