Android 中所有的视图都是通过 Window
来呈现的,像常用的 Activity,Dialog 和 Toast 都是附加在 Window 上的,所以 Window 是 View 的直接管理者 。
Window 是个抽象概念,每一个 Window 对应着一个 ViewRootImpl
,Window 通过 ViewRootImpl 来和 View 建立联系。View 是 Window 存在的实体,只能通过 WindowManager 访问 Window。
WindowManager
是一个接口,继承 ViewManager 接口。主要用来管理窗口的一些状态、属性,View 的添加、删除、更新、消息收集和处理等。其唯一实现类是 WindowManagerImpl
,WindowManagerImpl 又委托给了 WindowManagerGlobal。
WindowManagerGlobal
中存储着四个 List,分别是 mViews
,mRoots
,mParams
,mDyingViews
。单例模式。
Window 的操作通过 IPC 通信交给 WindowManagerService 操作。
Window 相关类
Window: 是一个抽象类,提供了统一的外观和行为标准。作为顶级视图添加到 WindowManager 中,View 是依附于 Window 而存在的,对 View 进行管理。View 是 window 的存在形式,window 是 view 的载体
PhoneWindow: Window 的唯一实现类。Activity,Toast 和 Dialog 都包含 PhoneWindow 对象,PopupWindow 。
WindowManager: 是一个接口,继承了 ViewManager(addView,updateViewLayout 和 removeView 方法) 接口,对 Window 进行管理。实现类是 WindowManagerImpl。
WindowManagerGlobal: 单例模式。对 Window 的操作都通过它代理执行。
ViewRootImpl: 实现了 ViewParent 接口,是 Window 和 DecorView 的桥梁,类似 ActivityThread
和 AMS 通信一样。内部类 W 是一个 Binder 对象,它通静态变量 sWindowSession(Session 实例
与 WMS 进行通信。绘制 View 和 事件传递。
Activity 是控制器。Window 是承载器,承载视图。DecorView 是顶层视图,负责视图部分。ViewRoot 是连接器,负责用户和 WMS 的交互。
Window LayoutParams 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public static class LayoutParams extends ViewGroup .LayoutParams implements Parcelable { public static final int FIRST_APPLICATION_WINDOW = 1 ; public static final int TYPE_BASE_APPLICATION = 1 ; public static final int LAST_APPLICATION_WINDOW = 99 ; public static final int FIRST_SUB_WINDOW = 1000 ; public static final int LAST_SUB_WINDOW = 1999 ; public static final int FIRST_SYSTEM_WINDOW = 2000 ; public static final int LAST_SYSTEM_WINDOW = 2999 ; public int x; public int y; public int type; public int flags; public IBinder token = null ; }
Window Type Window 有三种类型,分别是应用 Window
、子Window
、系统 Window
。 Window 是分层的,应用 Window 是 1-99,子 Window 是 1000-1999,系统 Window 是 2000-2999。层级大的会覆盖层级小的 Window 上面。
应用窗口:
Activity 对应的窗口类型是应用窗口, 所有 Activity 默认的窗口类型是 TYPE_BASE_APPLICATION
。 WindowManager 的 LayoutParams 的默认类型是 TYPE_APPLICATION
。 Dialog 并没有设置 type,所以也是默认的窗口类型即 TYPE_APPLICATION。
子窗口:
子窗口不能单独存在,它需要附属在父 Window 中,比如 PopupWindow 或 Dialog。
系统窗口:
一般来讲,系统窗口应该由系统来创建的,例如 ANR 时的提示框,系统状态栏,屏保等。但是,Framework 还是定义了一些,可以被应用所创建的系统窗口,如 TYPE_APPLICATION_OVERLAY。系统窗口需要相应的权限。
常量值
类型
FIRST_APPLICATION_WINDOW=1
第一个普通应用窗口
TYPE_BASE_APPLICATION=1
所有程序窗口的 base 窗口,其他应用程序窗口都显示在它上面
TYPE_APPLICATION=2
普通应用程序窗口,token 必须设置为 Activity 的 token 来指定窗口属于谁
TYPE_APPLICATION_STARTING=3
应用程序启动时先显示此窗口,当真正的窗口配置完成后,关闭此窗口
LAST_APPLICATION_WINDOW=99
最后一个应用窗口
FIRST_SUB_WINDOW = 1000
第一个子 Window
LAST_SUB_WINDOW = 1999
最后一个子 Window
FIRST_SYSTEM_WINDOW=2000
第一个系统窗口
TYPE_STATUS_BAR=2000
状态栏,只能有一个状态栏,位于屏幕顶端
TYPE_SYSTEM_DIALOG=2008
系统对话框
TYPE_INPUT_METHOD=2011
输入法窗口,显示于普通应用/子窗口之上
TYPE_APPLICATION_OVERLAY=2038
应用弹窗
LAST_SYSTEM_WINDOW=2999
最后一个系统窗口
注意: TYPE_PHONE、TYPE_SYSTEM_ALERT、TYPE_TOAST、TYPE_SYSTEM_OVERLAY、TYPE_PRIORITY_PHONE、TYPE_SYSTEM_ERROR 在 API 26 中已废弃,使用 TYPE_APPLICATION_OVERLAY 代替,需要申请 Manifest.permission.SYSTEM_ALERT_WINDOW 权限。 TYPE_KEYGUARD 已经被从系统中移除,使用 TYPE_KEYGUARD_DIALOG 代替。
Window Flag Window 属性用来控制 Window 的显示特性,这里主要介绍几个比较常用的选项:
Window 类型和标记通过 WindowManager.LayoutParams 设置:
1 2 3 4 5 6 7 public static class LayoutParams extends ViewGroup .LayoutParams implements Parcelable { public LayoutParams () { super (LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; format = PixelFormat.OPAQUE; } }
Window Token 在源码中 token 一般代表的是 Binder 对象,用于 IPC 通讯。并且它也包含着此次通讯所需要的信息,在 ViewRootImpl 里,token 用来表示 mWindow(W 类,即 IWindow),并且在 WMS 中只有符合要求的 token 才能让 Window 正常显示。
在 Window 中,token(LayoutParams.token)分为以下 2 种情况:
应用窗口 : 表示的是 activity 的 mToken
子窗口 : 表示的是父窗口的 W 对象,也就是 mWindow(IWindow)
Window 添加 View 的过程 1 2 3 4 5 public void setContentView (@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 public class PhoneWindow extends Window implements MenuBuilder .Callback { private DecorView mDecor; ViewGroup mContentParent; public void setContentView (int layoutResID) { if (mContentParent == null ) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true ; } private void installDecor () { if (mDecor == null ) { mDecor = generateDecor(-1 ); } else { mDecor.setWindow(this ); } if (mContentParent == null ) { mContentParent = generateLayout(mDecor); } } protected DecorView generateDecor (int featureId) { return new DecorView (context, featureId, this , getAttributes()); } protected ViewGroup generateLayout (DecorView decor) { TypedArray a = getWindowStyle(); WindowManager.LayoutParams params = getAttributes(); int layoutResource; int features = getLocalFeatures(); if (features & xxx != 0 ){ layoutResource = R.layout.xxx; } else { layoutResource = R.layout.screen_simple; } mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); mDecor.finishChanging(); return contentParent; } }
1 2 3 4 5 6 7 8 9 10 11 void onResourcesLoaded (LayoutInflater inflater, int layoutResource) { mDecorCaptionView = createDecorCaptionView(inflater); final View root = inflater.inflate(layoutResource, null ); if (mDecorCaptionView != null ) { } else { addView(root, 0 , new ViewGroup .LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mContentRoot = (ViewGroup) root; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:layout_width ="match_parent" android:layout_height ="match_parent" android:fitsSystemWindows ="true" android:orientation ="vertical" > <ViewStub android:id ="@+id/action_mode_bar_stub" android:inflatedId ="@+id/action_mode_bar" android:layout ="@layout/action_mode_bar" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:theme ="?attr/actionBarTheme" /> <FrameLayout android:id ="@android:id/content" android:layout_width ="match_parent" android:layout_height ="match_parent" android:foregroundInsidePadding ="false" android:foregroundGravity ="fill_horizontal|top" android:foreground ="?android:attr/windowContentOverlay" /> </LinearLayout >
首次调用 Activity#setContentView() 时通过 installDecor() 方法初始化 mDecor 和 mContentParent(R.id.content) 对象。
generateDecor() 创建 DecorView 对象。
generateLayout(mDecor) 填充布局。首先根据当前的主题设置相关 feature,获取布局文件(默认为 screen_simple.xml),然后将布局添加到 DecorView 中,并将 mContentParent 与布局中 id=R.id.content 的 FrameLayout 绑定。
如果设置了 FEATURE_CONTENT_TRANSITIONS,就会创建 Scene 完成转场动画;否则使用布局填充器将布局文件填充至 mContentParent。Activity 的布局文件也就添加到 DecorView 里面了。
Window 的创建过程 Activity 通过 ClassLoader 实例化后调用 Activity#attach() 方法,在该方法中创建 PhoneWindow 对象。
1 2 3 4 5 6 7 public final class ActivityThread extends ClientTransactionHandler { private Activity performLaunchActivity (ActivityClientRecord r, Intent customIntent) { activity.attach(appContext, this , getInstrumentation(), ...); mInstrumentation.callActivityOnCreate(activity, r.state); } }
通过 AMS 启动 Activity,回调 performLaunchActivity() 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Activity extends ContextThemeWrapper implements Window .Callback, Window.OnWindowDismissedCallback ...{ private Thread mUiThread; ActivityThread mMainThread; private IBinder mToken; private Window mWindow; private WindowManager mWindowManager; final void attach (Context context, ActivityThread aThread, IBinder token,...) { mWindow = new PhoneWindow (this , window, activityConfigCallback); mWindow.setCallback(this ); mWindow.setOnWindowDismissedCallback(this ); mMainThread = aThread; mToken = token; mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, ...); mWindowManager = mWindow.getWindowManager(); } @Override public Object getSystemService (@ServiceName @NonNull String name) { if (WINDOW_SERVICE.equals(name)) { return mWindowManager; } else if (SEARCH_SERVICE.equals(name)) { ensureSearchManager(); return mSearchManager; } return super .getSystemService(name); } }
在 Activity#onCreate() 方法里,我们通过 setContentView() 将 view 添加到 PhoneWindow 的 mContentParent(R.id.content) 中,也就是将资源布局文件和 PhoneWindow 关联。虽然 Window 和 DecorView 已经创建并初始化完毕,但这个时候的 DecorView 还没有被 WindowManager 添加到 Window 中。
Window 的添加过程 PhoneWindow 只是负责处理一些应用窗口通用的逻辑(设置标题栏,导航栏等)。真正完成把一个 View 作为窗口添加到 WMS 的过程是由 WindowManager 来完成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public void handleResumeActivity (IBinder token, ...) { final ActivityClientRecord r = performResumeActivity(token, ...); final Activity a = r.activity; if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true ; wm.addView(decor, l); } } } if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } } }
通过 WindowManagerImpl 将 DecorView 添加到窗口上 wm.addView(decor, l)
,又交给了 WindowManagerGlobal 添加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public final class WindowManagerGlobal { private static IWindowManager sWindowManagerService; private static IWindowSession sWindowSession; public void addView (View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) { if (parentWindow != null ) { parentWindow.adjustLayoutParamsForSubWindow(wparams); } ViewRootImpl root; View panelParentView = null ; synchronized (mLock) { 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.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); root.setView(view, wparams, panelParentView, userId); } } public static IWindowManager getWindowManagerService () { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null ) { sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window" )); } return sWindowManagerService; } } public static IWindowSession getWindowSession () { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null ) { IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( new IWindowSessionCallback .Stub() { @Override public void onAnimatorScaleChanged (float scale) { ValueAnimator.setDurationScale(scale); } }); } return sWindowSession; } } }
接着通过 ViewRootImpl#setView() 设置 DecorView。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public final class ViewRootImpl implements ViewParent ...{ final IWindowSession mWindowSession; final View.AttachInfo mAttachInfo; public ViewRootImpl (Context context, Display display) { this (context, display, WindowManagerGlobal.getWindowSession(), false ); } public ViewRootImpl (Context context, Display display, IWindowSession session, boolean useSfChoreographer) { mWindowSession = session; mWindow = new W (this ); mAttachInfo = new View .AttachInfo(mWindowSession, mWindow ...); } public void setView (View view, WindowManager.LayoutParams attrs, ...) { mView = view; mAdded = true ; requestLayout(); int res = mWindowSession.addToDisplayAsUser(mWindow, ...) if (res < WindowManagerGlobal.ADD_OKAY) { switch (res) { case WindowManagerGlobal.ADD_BAD_APP_TOKEN: case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN: throw new WindowManager .BadTokenException( "Unable to add window -- token " + attrs.token + " is not valid; is your activity running?" ); case WindowManagerGlobal.ADD_DUPLICATE_ADD: throw new WindowManager .BadTokenException( "Unable to add window -- window " + mWindow + " has already been added" ); case WindowManagerGlobal.ADD_PERMISSION_DENIED: throw new WindowManager .BadTokenException("Unable to add window " + mWindow + " -- permission denied for window type " + mWindowAttributes.type); case WindowManagerGlobal.ADD_INVALID_TYPE: throw new WindowManager .InvalidDisplayException("Unable to add window " + mWindow + " -- the specified window type " + mWindowAttributes.type + " is not valid" ); ... } throw new RuntimeException ( "Unable to add window -- unknown error code " + res); } view.assignParent(this ); } static class W extends IWindow .Stub { @Override public void insetsChanged (InsetsState insetsState) {} } }
mWindowSession 通过 WindowManagerGlobal.getWindowSession()
方法获取,是一个 Binder 对象。因为 WindowManagerGlobal 是单例,所以 mWindowSession 对象每个进程只有一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Session extends IWindowSession .Stub implements IBinder .DeathRecipient { public Session (WindowManagerService service, IWindowSessionCallback callback) {} @Override public int addToDisplayAsUser (IWindow window, ...) { return mService.addWindow(this , window, seq, ...); } }
每一个 Session 在 WidowManagerService 里代表一个与应用进程打开的会话连接,每个进程只有一个 Session 对象。最终 Session 通过 IPC 通信,请求 WMS 添加 Window,并由 W 类接收通知。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class WindowManagerService extends IWindowManager .Stub...{ final ArraySet<Session> mSessions = new ArraySet <>(); final HashMap<IBinder, WindowState> mWindowMap = new HashMap <>(); WindowManagerPolicy mPolicy; public int addWindow (Session session, IWindow client, ...) { int res = mPolicy.checkAddPermission(attrs.type, ...); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { } final WindowState win = new WindowState (this , session, client, token, ...); if (type == TYPE_TOAST) { mH.sendMessageDelayed( mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win), win.mAttrs.hideTimeoutMilliseconds); } res = WindowManagerGlobal.ADD_OKAY; win.attach(); mWindowMap.put(client.asBinder(), win); return res } }
Window 的添加请求就交给 WMS 去处理了,在 WMS 内部会为每一个应用保留一个单独的 Session,同时会创建一个 WindowState 对象来表示当前添加的窗口。 无论是应用窗口还是子窗口,token 不能为空。否则会抛出异常,并且应用窗口的 token 必须是某个 Activity 的 mToken,子窗口的 token 必须是父窗口的 IWindow 对象,而且子窗口还需等父窗口添加成功之后才可添加到 Window 上。
Window 添加流程图如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 public class PopupWindow { public PopupWindow (Context context, AttributeSet attrs, ...) { mContext = context; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } public void setContentView (View contentView) { if (isShowing()) { return ; } mContentView = contentView; } public void showAsDropDown (View anchor, int xoff, int yoff, int gravity) { if (isShowing() || !hasContentView()) { return ; } attachToAnchor(anchor, xoff, yoff, gravity); final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getApplicationWindowToken()); preparePopup(p); final boolean aboveAnchor = findDropDownPosition(anchor, ...); updateAboveAnchor(aboveAnchor); invokePopup(p); } private void preparePopup (WindowManager.LayoutParams p) { if (mContentView == null || mContext == null || mWindowManager == null ) { throw new IllegalStateException ("You must specify a valid content view by " + "calling setContentView() before attempting to show the popup." ); } mDecorView = createDecorView(mBackgroundView); } private PopupDecorView createDecorView (View contentView) { final PopupDecorView decorView = new PopupDecorView (mContext); decorView.addView(contentView, MATCH_PARENT, height); return decorView; } private void invokePopup (WindowManager.LayoutParams p) { final PopupDecorView decorView = mDecorView; mWindowManager.addView(decorView, p); } public void dismiss () { if (!isShowing() || isTransitioningToDismiss()) { return ; } dismissImmediate(decorView, contentHolder, contentView); detachFromAnchor(); if (mOnDismissListener != null ) { mOnDismissListener.onDismiss(); } } private class PopupDecorView extends FrameLayout {} }
创建 PopupWindow,设置 ContentView 等其他属性。
调用 showAtLocation() 或 showAsDropDown() 方法,通过 WindowManager 添加 View。
注意:PopupWindow 并没有创建 PhoneWindow 对象。它和 Activity 在同一层级,不会在 Activity 上方。
Dialog 中的 Window 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class Dialog implements DialogInterface , Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback { Dialog(@NonNull Context context, @StyleRes int themeResId,...) { mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final Window w = new PhoneWindow (mContext); mWindow = w; w.setCallback(this ); w.setOnWindowDismissedCallback(this ); w.setOnWindowSwipeDismissedCallback(() -> { if (mCancelable) { cancel(); } }); w.setWindowManager(mWindowManager, null , null ); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler (this ); } public void setContentView (@NonNull View view) { mWindow.setContentView(view); } public void show () { if (mShowing) { if (mDecor != null ) { mDecor.setVisibility(View.VISIBLE); } return ; } mCanceled = false ; dispatchOnCreate(null ); onStart(); mDecor = mWindow.getDecorView(); WindowManager.LayoutParams l = mWindow.getAttributes(); mWindowManager.addView(mDecor, l); mShowing = true ; sendShowMessage(); } private static final class ListenersHandler extends Handler {} }
Dialog 是子窗口,依赖 Activity,需要 Activity 的 token,如果传递 ApplicationContext 会报 android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
错误。
总结
在 Window 系统中,分为两部分的内容:一部分是运行在系统服务进程的 WMS 及相关类,WMS 用 WindowState 来描述一个窗口;另一部分是运行在应用进程的 WindowManagerImpl, WindowManagerGlobal,ViewRootImpl 等相关类。
对于 WMS 来讲,窗口对应一个 View 对象,而不是 Window 对象。
Window 是抽象类,描述是一类具有某种通用特性的窗口,唯一实现类是 PhoneWindow。PhoneWindow 类把一些操作的统一处理了,例如长按,按”Back”键等。
Android Framework 把窗口分为三种类型,应用窗口,子窗口以及系统窗口。不同类型的窗口在执行添加窗口操作时,对于 WindowManager.LayoutParams 的 token 参数有不同的要求。 应用窗口必须是某个有效的 Activity 的 mToken;子窗口的 token 必须是父窗口的 ViewRootImpl 中的 W 对象;有些系统窗口不需要 token,有些系统窗口的 token 必须满足一定的要求。
只能通过 Context.getSystemServer() 来获取 WindowManager。Activity 返回 mWindowManager,即 WindowManagerImpl。
在调用 WindowManagerImpl 的 addView 之前,如果没有给 token 赋值,则会走默认的 token 赋值逻辑。 默认的 token 是如果 mParentWindow 不为空,则会调用其 adjustLayoutParamsForSubWindow() 方法。在 adjustLayoutParamsForSubWindow() 方法中,如果当前要添加的窗口是应用窗口,如果其 token 为空,则会把当前 PhoneWindow 的 mToken 赋值给 token。如果是子窗口,则会把当前 PhonwWindow 对应的 DecorView 的 mAttachInfo 中的 mWindowToken 赋值给 token。而 View 中的 mAttachIno 来自 ViewRootImpl 的 mAttachInfo。因此这个 token 本质就是父窗口的 ViewRootImpl 中的 W 类对象。
参考 [1] Android Window 机制探索 [2] Android 窗口创建流程