本文最后更新于 880 天前,其中的信息可能已经有所发展或是发生改变。
- Activity拥有自己PhoneWindow以及WindowManager,同时它的PhoneWindow拥有token;而Application并没有自己的PhoneWindow,他返回的WindowManager是应用服务windowManager,并没有赋值token的过程
-
token是个Binder对象,他持有ActivityRecord的弱引用,这样可以访问到activity的所有信息
-
每个token创建后,会在后续发送到WMS ,WMS对token进行缓存,而后续对于应用发送来的token只需要在缓存拿出来匹配一下就知道是否合法了
AMS创建ActivityRecord,其构造器生成Token,1. 将token存到Activity的PhoneWindow,后续添加View或对View操作时,获取其Token进行后续比对。2. 将Token同进程给WMS,WMS拿到Token会生成WindowToken并缓存 且以IBInder为key,WindowToken为Value进行map存储。
- token在创建ActivityRecord的时候一起被创建,他是一个IBinder对象,实现了接口IApplicationToken。
- token创建后会发送到WMS,在WMS中封装成WindowToken,并存在一个HashMap<IBinder,WindowToken>。
- token会随着ActivityRecord被发送到本地进程,ActivityThread根据AMS的指令执行Activity启动逻辑。
- Activity启动的过程中会创建PhoneWindow和对应的WindowManager,同时把token存在PhoneWindow中。
- 通过Activity的WindowManager添加view/弹出dialog时会把PhoneWindow中的token放在窗口LayoutParams中。
- 通过viewRootImpl向WMS进行验证,WMS在LayoutParams拿到IBinder之后就可以在Map中获取WindowToken。
- 根据获取的结果就可以判断该token的合法情况。
ViewRootImpl.class(api29)
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
int res;
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
...
if (res < WindowManagerGlobal.ADD_OKAY) {
...
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
/*
* 1
*/
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
...
}
...
}
...
}
Session.class(api29)
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
outInsetsState);
}
}
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState) {
...
WindowState parentWindow = null;
...
// 获取parentWindow
parentWindow = windowForClientLocked(null, attrs.token, false);
...
final boolean hasParent = parentWindow != null;
// 获取token
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
...
// 验证token
if (token == null) {
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
...//各种验证
}
...
}
- 开启硬件加速,在 AndroidO 及之后的版本会间接调用 onDescendantInvalidated 触发 UI 刷新,该逻辑躲过了系统 checkThread 检查,将会造成线程并发隐患。如下图,如果并发执行则会导致前一个线程的 mTraversalBarrier 被覆盖,从而导致 vsync 消息与 barrier 出现同步问题.
@Override
public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
mIsAnimating = true;
}
invalidate();
}