近期工作中遇到一个问题,就是在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);
}
}
}
如果你看到了这里,觉得文章写得不错就给个赞呗?
更多Android进阶指南 可以扫码 解锁更多Android进阶资料
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )