class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>
class RootWindowContainer extends WindowContainer<DisplayContent>
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
class WindowState extends WindowContainer<WindowState>
class WindowToken extends WindowContainer<WindowState>
final class ActivityRecord extends WindowToken
class Task extends WindowContainer<WindowContainer>
class ActivityStack extends Task
参考:www.kancloud.cn/alex_wsc/an…
final class ActivityRecord extends WindowToken
在ActivityRecord的构造方法中,先调用父类WindowToken的构造函数,得到其WindowToken
因此,ActivityRecord的apptoken(IApplicationToken.Stub)是等价于WindowToken的(强制类型转换)
super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
null /* displayContent */, false /* ownerCanManageAppTokens */);
mAtmService = _service;
appToken = (Token) token;
info = aInfo;
mUserId = UserHandle.getUserId(info.applicationInfo.uid);
packageName = info.applicationInfo.packageName;
对于显示组件(客户端)而言的Token,是任意一个Binder的实例,对显示组件(客户端)来说仅仅是一个创建窗口的令牌,没有其他的含义。
Token(Intent intent) {
name = intent.getComponent().flattenToShortString();
tokenString = "Token{" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
WindowToken由token和WindowType来区分,已没有AppWindowToken这个类。添加 WindowToken,也是根据WindowType来添加
class WindowToken extends WindowContainer<WindowState> {
// The actual token.
final IBinder token;
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
WindowState是WMS中的窗口实例,其mToken保存了窗口令牌,其ActivityRecord保存了对应的Activity(如果是应用窗口的话,不是应用窗口,则为null)
class WindowState extends WindowContainer<WindowState>
WindowToken mToken;
// The same object as mToken if this is an app window and null for non-app windows.
ActivityRecord mActivityRecord;
WindowToken的构造方法
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
boolean roundedCornerOverlay, boolean fromClientToken) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mOwnerUid = ownerUid;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this);
}
最后,是在DisplayContent中添加WindowToken
//DisplayContent.java
void addWindowToken(IBinder binder, WindowToken token) {
mTokenMap.put(binder, token);
//如果调用ActivityRecord#asActivityRecord为空,说明其不是Activity的Token
if (token.asActivityRecord() == null) {
总结:ActivityRecord----->apptoken----->WindowToken----->WindowState
可以由WindowState来处理ActivityRecord的可见性等问题
2020-11-24
ViewManager这个接口,只有三个方法,添加view,updateView布局,删除View
public interface ViewManager {
void addView(View var1, LayoutParams var2);
void updateViewLayout(View var1, LayoutParams var2);
void removeView(View var1);
}
The interface that apps use to talk to the window manager
一个WindowManager实例对应一个Display,获取WindowManager需要Context上下文(Context.getSystemService(Context.WINDOW_SERVICE))
对应其注释
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
窗口的布局参数,这个是很重要的
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
@WindowType
public int type;
//分为三种,APPLICATION,FIRST_SUB_WINDOW,SYSTEM,在WMS的方法addWindow时,根据其窗口类型的不同,其窗口得到不同WindoToken
这里,简单介绍一下处理的一个故障,与flsgs相关:
/** Window flag: as long as this window is visible to the user, keep
* the device's screen turned on and bright. */
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
值得注意的是,只要Activity对应的WindowState带有这个标志位,即使Activity没有带点亮屏幕的标志位,屏幕也会被对应的WindowState点亮,这是R上新增的亮屏逻辑(PowerManagerservice的wakeUpNoUpdateLocked方法有点亮屏幕的原因)
有一些业务场景需要注意这个:如近感灭屏,第三方通话,Q上不会亮屏,R上会亮屏。Google dialer 52如前所述,最新的dialer已修改这个bug,需要应用做亮屏适配
Activity方面,主要是R上新增的接口containsTurnScreenOnWindow
boolean getTurnScreenOnFlag() {
return mTurnScreenOn || containsTurnScreenOnWindow();
}
private boolean containsTurnScreenOnWindow() {
// When we are relaunching, it is possible for us to be unfrozen before our previous
// windows have been added back. Using the cached value ensures that our previous
// showWhenLocked preference is honored until relaunching is complete.
if (isRelaunching()) {
return mLastContainsTurnScreenOnWindow;
}
//可以在这里添加打印log,便比较清楚了
for (int i = mChildren.size() - 1; i >= 0; i--) {
if ((mChildren.get(i).mAttrs.flags & LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
return true;
}
}
return false;
}
WindowState方面,主要新增的亮屏判断hasTurnScreenOnFlag
void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
// We need to turn on screen regardless of visibility.
final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0
|| (mActivityRecord != null && mActivityRecord.canTurnScreenOn());
这里再介绍一个处理的故障,与窗口令牌有关,主要是子窗口的可见性问题
IBinder,这个是窗口令牌。但system窗口的这个属性没有,即mAttrs.token = null。所以在系统窗口上,添加popupWindow,会导致子窗口没有令牌,从而子窗口显示不出来
/**
* Identifier for this window. This will usually be filled in for
* you.
*/
public IBinder token = null;
在WMS#addWindow中
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
需要做一定的修改:
Ibinder parentWindow_Token = new Binder();
if (hasParent){
parentWindow_Token = parentWindow.mAttrs.token != null ? parentWindow.mAttrs.token : parentWindow.mToken.token;
}
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow_Token : attrs.token);
packageName这个参数不为null,如果为null,会在ViewRootImpl的setView为其赋值的。
/**
* Name of the package owning this window.
*/
public String packageName = null;
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
layoutInDisplayCutoutMode 刘海屏控制flag,应用布局,是否占用Notch区域。真正的全面屏,是没有Notch,市面的全面屏,都是根据这个flag,拉伸应用布局,使其占用Notch区域
参考资料:blog.csdn.net/yi_master/a…
/**
* Controls how the window is laid out if there is a {@link DisplayCutout}.
*
* <p>
* Defaults to {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}.
*
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
* @see DisplayCutout
* @see android.R.attr#windowLayoutInDisplayCutoutMode
* android:windowLayoutInDisplayCutoutMode
*/
@LayoutInDisplayCutoutMode
public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
2020-11-25
WindowManagerImpl 成员变量有上下文和父窗口
Provides low-level communication with the system window manager for
operations that are bound to a particular context, display or parent window.
public final class WindowManagerImpl implements WindowManager {
@UnsupportedAppUsage
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@VisibleForTesting
public final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
addView
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
/**
// Only use the default token if we don't have a parent window.
if (mDefaultToken != null && mParentWindow == null) {
*/
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
WindowManagerGlobal不带有Context上下文,有WMS和WindowSession的代理
/**
* Provides low-level communication with the system window manager for
* operations that are not associated with any particular context.
@UnsupportedAppUsage
private static IWindowManager sWindowManagerService;
@UnsupportedAppUsage
private static IWindowSession sWindowSession;
保存了添加的所有窗口,RootView及其布局参数
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//Global是单实例对象,一个进程只有一个
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
addView的部分流程分析
// If this is a panel window, then find the window it is being
// attached to for future reference.
//如果其type是子窗口,需要找到其父窗口
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {//窗口令牌相等
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);//根据View的上下文新建ViewRootImpl
view.setLayoutParams(wparams);
//mView,mRoots,mParams,三个数组一一对应
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView, userId);
WindowManagerGlobal的大部分功能,都是由ViewRootImpl来完成的。ViewRootImpl是连接客户端与服务端的大管家。
/ * The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* detail of {@link WindowManagerGlobal}.
*/
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
AndroidR上,默认为2,所以,使用 {@link InsetsController}来控制status/navigation bar
public static int sNewInsetsMode =
SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, NEW_INSETS_MODE_FULL);
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;//顶层View,即DecorView,使用mView保存
//...
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();//先发出一个请求布局的Handler消息,在添加窗口过后,布局
//...
//向WMS添加窗口的起点
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
setFrame(mTmpFrame);//WMS初步布局返回的mWinFrame
//...
view.assignParent(this);//分配View的根节点
mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
2020-11-29
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}