做网站看什么书好,海外商城网站建设,公共资源交易中心编制,美食网站开发的目的和意义一、前言
在 android framework 框架中 activity 和 window 是相互关联的#xff0c;而他们的管理者 AMS 和 WMS 是怎么来实现这种关联关系的#xff0c;答案就是通过 token。
首先大家需要了解一下 LayoutParams#xff0c;当然属性很多#xff0c;简单了解即可#xf…一、前言
在 android framework 框架中 activity 和 window 是相互关联的而他们的管理者 AMS 和 WMS 是怎么来实现这种关联关系的答案就是通过 token。
首先大家需要了解一下 LayoutParams当然属性很多简单了解即可
base/core/java/android/view/WindowManager.java
...//窗口类型//有3种主要类型如下//ApplicationWindows取值在FIRST_APPLICATION_WINDOW与LAST_APPLICATION_WINDOW之间是常用的顶层应用程序窗口须将token设置成Activity的token//SubWindows取值在FIRST_SUB_WINDOW和LAST_SUB_WINDOW之间与顶层窗口相关联需将token设置成它所附着宿主窗口的token//SystemWindows取值在FIRST_SYSTEM_WINDOW和LAST_SYSTEM_WINDOW之间不能用于应用程序使用时需要有特殊权限它是特定的系统功能才能使用public int type;//WindowType开始应用程序窗口public static final int FIRST_APPLICATION_WINDOW 1;//WindowType所有程序窗口的base窗口其他应用程序窗口都显示在它上面public static final int TYPE_BASE_APPLICATION 1;//WindowType普通应用程序窗口token必须设置为Activity的token来指定窗口属于谁public static final int TYPE_APPLICATION 2;//WindowType应用程序启动时所显示的窗口应用自己不要使用这种类型它被系统用来显示一些信息直到应用程序可以开启自己的窗口为止public static final int TYPE_APPLICATION_STARTING 3;//WindowType结束应用程序窗口public static final int LAST_APPLICATION_WINDOW 99;//WindowTypeSubWindows子窗口子窗口的Z序和坐标空间都依赖于他们的宿主窗口public static final int FIRST_SUB_WINDOW 1000;//WindowType 面板窗口显示于宿主窗口的上层public static final int TYPE_APPLICATION_PANEL FIRST_SUB_WINDOW;//WindowType媒体窗口例如视频显示于宿主窗口下层public static final int TYPE_APPLICATION_MEDIA FIRST_SUB_WINDOW1;//WindowType应用程序窗口的子面板显示于所有面板窗口的上层public static final int TYPE_APPLICATION_SUB_PANEL FIRST_SUB_WINDOW2;//WindowType对话框类似于面板窗口绘制类似于顶层窗口而不是宿主的子窗口public static final int TYPE_APPLICATION_ATTACHED_DIALOG FIRST_SUB_WINDOW3;//WindowType媒体信息显示在媒体层和程序窗口之间需要实现半透明效果public static final int TYPE_APPLICATION_MEDIA_OVERLAY FIRST_SUB_WINDOW4;//WindowType子窗口结束public static final int LAST_SUB_WINDOW 1999;//WindowType系统窗口非应用程序创建public static final int FIRST_SYSTEM_WINDOW 2000;//WindowType状态栏只能有一个状态栏位于屏幕顶端其他窗口都位于它下方public static final int TYPE_STATUS_BAR FIRST_SYSTEM_WINDOW;//WindowType搜索栏只能有一个搜索栏位于屏幕上方public static final int TYPE_SEARCH_BAR FIRST_SYSTEM_WINDOW1;//WindowType电话窗口它用于电话交互特别是呼入置于所有应用程序之上状态栏之下public static final int TYPE_PHONE FIRST_SYSTEM_WINDOW2;//WindowType系统提示出现在应用程序窗口之上public static final int TYPE_SYSTEM_ALERT FIRST_SYSTEM_WINDOW3;//WindowType锁屏窗口public static final int TYPE_KEYGUARD FIRST_SYSTEM_WINDOW4;//WindowType信息窗口用于显示Toastpublic static final int TYPE_TOAST FIRST_SYSTEM_WINDOW5;//WindowType系统顶层窗口显示在其他一切内容之上此窗口不能获得输入焦点否则影响锁屏public static final int TYPE_SYSTEM_OVERLAY FIRST_SYSTEM_WINDOW6;//WindowType电话优先当锁屏时显示此窗口不能获得输入焦点否则影响锁屏public static final int TYPE_PRIORITY_PHONE FIRST_SYSTEM_WINDOW7;//WindowType系统对话框public static final int TYPE_SYSTEM_DIALOG FIRST_SYSTEM_WINDOW8;//WindowType锁屏时显示的对话框public static final int TYPE_KEYGUARD_DIALOG FIRST_SYSTEM_WINDOW9;//WindowType系统内部错误提示显示于所有内容之上public static final int TYPE_SYSTEM_ERROR FIRST_SYSTEM_WINDOW10;//WindowType内部输入法窗口显示于普通UI之上应用程序可重新布局以免被此窗口覆盖public static final int TYPE_INPUT_METHOD FIRST_SYSTEM_WINDOW11;//WindowType内部输入法对话框显示于当前输入法窗口之上public static final int TYPE_INPUT_METHOD_DIALOG FIRST_SYSTEM_WINDOW12;//WindowType墙纸窗口public static final int TYPE_WALLPAPER FIRST_SYSTEM_WINDOW13;//WindowType状态栏的滑动面板public static final int TYPE_STATUS_BAR_PANEL FIRST_SYSTEM_WINDOW14;//WindowType安全系统覆盖窗口这些窗户必须不带输入焦点否则会干扰键盘public static final int TYPE_SECURE_SYSTEM_OVERLAY FIRST_SYSTEM_WINDOW15;//WindowType拖放伪窗口只有一个阻力层(最多)它被放置在所有其他窗口上面public static final int TYPE_DRAG FIRST_SYSTEM_WINDOW16;//WindowType状态栏下拉面板public static final int TYPE_STATUS_BAR_SUB_PANEL FIRST_SYSTEM_WINDOW17;//WindowType鼠标指针public static final int TYPE_POINTER FIRST_SYSTEM_WINDOW18;//WindowType导航栏(有别于状态栏时)public static final int TYPE_NAVIGATION_BAR FIRST_SYSTEM_WINDOW19;//WindowType音量级别的覆盖对话框显示当用户更改系统音量大小public static final int TYPE_VOLUME_OVERLAY FIRST_SYSTEM_WINDOW20;//WindowType起机进度框在一切之上public static final int TYPE_BOOT_PROGRESS FIRST_SYSTEM_WINDOW21;//WindowType假窗消费导航栏隐藏时触摸事件public static final int TYPE_HIDDEN_NAV_CONSUMER FIRST_SYSTEM_WINDOW22;//WindowType梦想(屏保)窗口略高于键盘public static final int TYPE_DREAM FIRST_SYSTEM_WINDOW23;//WindowType导航栏面板(不同于状态栏的导航栏)public static final int TYPE_NAVIGATION_BAR_PANEL FIRST_SYSTEM_WINDOW24;//WindowTypeuniverse背后真正的窗户public static final int TYPE_UNIVERSE_BACKGROUND FIRST_SYSTEM_WINDOW25;//WindowType显示窗口覆盖用于模拟辅助显示设备public static final int TYPE_DISPLAY_OVERLAY FIRST_SYSTEM_WINDOW26;//WindowType放大窗口覆盖用于突出显示的放大部分可访问性放大时启用public static final int TYPE_MAGNIFICATION_OVERLAY FIRST_SYSTEM_WINDOW27;//WindowType......public static final int TYPE_KEYGUARD_SCRIM FIRST_SYSTEM_WINDOW29;public static final int TYPE_PRIVATE_PRESENTATION FIRST_SYSTEM_WINDOW30;public static final int TYPE_VOICE_INTERACTION FIRST_SYSTEM_WINDOW31;public static final int TYPE_ACCESSIBILITY_OVERLAY FIRST_SYSTEM_WINDOW32;//WindowType系统窗口结束public static final int LAST_SYSTEM_WINDOW 2999;......}这里需要我们知道的是 WindowManager.LayoutParams 有三种窗口 type分别对应为
应用窗口程序type 值在 FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW必须将 token 设置为 Activity 的 token。子窗口 type 值在 FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW SubWindows必须将 token 设置为 Activity 的 token。比如 PopupWindow。系统窗口 type 值在 FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW使用需要权限属于特定的系统功能。比如 Toast。
这里就说到了 token 的问题应用窗口程序和子窗口均需要获取到 Activity 的 token。那么 token 是什么呢
二、Token 是什么
Token 是 ActivityRecord 的静态内部类我们先来看下 Token 的继承关系Token 继承于IApplicationToken.Stub从 IApplicationToken.Stub 类进行继承根据 Binder 的机制可以知道 Token 是一个匿名 Binder 实体类这个匿名 Binder 实体会传递给其他进程其他进程会拿到 Token 的代理端。
我们知道匿名 Binder 有两个比较重要的用途一个是拿到 Binder 代理端后可跨 Binder 调用实体端的函数接口另一个作用便是在多个进程中标识同一个对象。往往这两个作用是同时存在的比如我们这里研究的 Token 就同时存在这两个作用但最重要的便是后者Token 标识了一个 ActivityRecord 对象即间接标识了一个 Activity。
base/service/core/java/com/android/server/wm/ActivityRecord.java
static class Token extends IApplicationToken.Stub {private WeakReferenceActivityRecord weakActivity;private final String name;private final String tokenString;Token(Intent intent) {name intent.getComponent().flattenToShortString();tokenString Token{ Integer.toHexString(System.identityHashCode(this)) };}private void attach(ActivityRecord activity) {if (weakActivity ! null) {throw new IllegalStateException(Already attached... this);}weakActivity new WeakReference(activity);}private static Nullable ActivityRecord tokenToActivityRecordLocked(Token token) {if (token null) {return null;}ActivityRecord r token.weakActivity.get();if (r null || r.getRootTask() null) {return null;}return r;}Overridepublic String toString() {StringBuilder sb new StringBuilder(128);sb.append(Token{);sb.append(Integer.toHexString(System.identityHashCode(this)));sb.append( );if (weakActivity ! null) {sb.append(weakActivity.get());}sb.append(});return sb.toString();}Overridepublic String getName() {return name;}}三、Token 的创建
private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,String _resultWho, int _reqCode, boolean _componentSpecified,boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor,ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState,TaskDescription _taskDescription, long _createTime) {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;intent _intent;。。。。。。在 ActivityRecord 的构造方法中创建这个 token 并赋值给成员变量 appToken。标识着当前这个ActivityRecord即间接代表着一个Activity。
而创建 ActivityRecord 的地方在 ActivityStarter.java 的 executeRequest 方法中可以参考 Android四大组件系列2 Activity启动流程上
ActivityStarter.java
private int executeRequest(Request request) {......final ActivityRecord r new ActivityRecord.Builder(mService).setCaller(callerApp).setLaunchedFromPid(callingPid).setLaunchedFromUid(callingUid).setLaunchedFromPackage(callingPackage).setLaunchedFromFeature(callingFeatureId).setIntent(intent).setResolvedType(resolvedType).setActivityInfo(aInfo).setConfiguration(mService.getGlobalConfiguration()).setResultTo(resultRecord).setResultWho(resultWho).setRequestCode(requestCode).setComponentSpecified(request.componentSpecified).setRootVoiceInteraction(voiceSession ! null).setActivityOptions(checkedOptions).setSourceRecord(sourceRecord).build();......
}ActivityRecord.java
private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,String _resultWho, int _reqCode, boolean _componentSpecified,boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor,ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState,TaskDescription _taskDescription, long _createTime) {super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,null /* displayContent */, false /* ownerCanManageAppTokens */);// 可知调用父类也就是 WindowToken 的构造方法并new了一个 token 作为参数传入mAtmService _service;appToken (Token) token;info aInfo;mUserId UserHandle.getUserId(info.applicationInfo.uid);packageName info.applicationInfo.packageName;intent _intent;
}WindowToken.java
protected WindowToken(WindowManagerService service, IBinder _token, int type,boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) {this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */);}protected WindowToken(WindowManagerService service, IBinder _token, int type,boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,boolean roundedCornerOverlay, boolean fromClientToken, Nullable Bundle options) {super(service);token _token;windowType type;mOptions options;mPersistOnEmpty persistOnEmpty;mOwnerCanManageAppTokens ownerCanManageAppTokens;mRoundedCornerOverlay roundedCornerOverlay;mFromClientToken fromClientToken;if (dc ! null) {dc.addWindowToken(token, this);}}WindowToken 是 ActivityRecord 的父类最终赋值给 WindowToken 的 token 变量 。
class WindowToken extends WindowContainerWindowState {private static final String TAG TAG_WITH_CLASS_NAME ? WindowToken : TAG_WM;/** The actual token */final IBinder token;/** The type of window this token is for, as per {link WindowManager.LayoutParams} */final int windowType;......
}以上 Token 的创建均在 AMS 服务端完成。
四、Token 怎么传递给客户端
可以参考 Android四大组件系列3 Activity启动流程下
ActivityTaskSupervisor.realStartActivityLocked
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,boolean andResume, boolean checkConfig) throws RemoteException {
......// Create activity launch transaction.final ClientTransaction clientTransaction ClientTransaction.obtain(proc.getThread(), r.appToken); // 这里的 r.appToken 就是 ActivityRecord 的 token
......// Schedule transaction.mService.getLifecycleManager().scheduleTransaction(clientTransaction);// 回传给客户端进程执行 ClientTransaction
}app 端通过 TransactionExecutor 来执行 ClientTransaction
public void execute(ClientTransaction transaction) {if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) Start resolving transaction);final IBinder token transaction.getActivityToken(); // 获取AMS传递过来的token......最后执行 LaunchActivityItem 的 execute
LaunchActivityItem.java
public void preExecute(ClientTransactionHandler client, IBinder token) {ActivityClientRecord r new ActivityClientRecord(token, mIntent, mIdent, mInfo,mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,mLaunchedFromBubble);// 通过 token 创建 ActivityClientRecord 至此绑定成功client.addLaunchingActivity(token, r);client.updateProcessState(mProcState, false);client.updatePendingConfiguration(mCurConfig);if (mActivityClientController ! null) {ActivityClient.setActivityClientController(mActivityClientController);}}随后调用 Activity 的 attach 方法会传入 token
Activity.java
final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,NonConfigurationInstances lastNonConfigurationInstances,Configuration config, String referrer, IVoiceInteractor voiceInteractor,Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,IBinder shareableActivityToken) {attachBaseContext(context);mFragments.attachHost(null /*parent*/);mWindow new PhoneWindow(this, window, activityConfigCallback);mWindow.setWindowControllerCallback(mWindowControllerCallback);mWindow.setCallback(this);mWindow.setOnWindowDismissedCallback(this);mWindow.getLayoutInflater().setPrivateFactory(this);if (info.softInputMode ! WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);}if (info.uiOptions ! 0) {mWindow.setUiOptions(info.uiOptions);}mUiThread Thread.currentThread();mMainThread aThread;mInstrumentation instr;mToken token; // token 保存到 activity 中......mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags ActivityInfo.FLAG_HARDWARE_ACCELERATED) ! 0);// 关键方法把 mToken 传递给 PhoneWindow 的 mAppTokenmWindowManager mWindow.getWindowManager(); // window 的WindowManager 赋值给了activity......通过 mWindow.setWindowManager 把 mToken 传递给 PhoneWindow 的成员变量 mAppToken。
这样就建立了 AMS 中的 ActivityRecord 与 客户端进程 Activity 以及对应的 PhoneWindow 之间的一一对应关系。
我们接下来看下 Window 的 setWindowManager 方法
Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {mAppToken appToken; // 赋值 tokenmAppName appName;mHardwareAccelerated hardwareAccelerated;if (wm null) {wm (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}mWindowManager ((WindowManagerImpl)wm).createLocalWindowManager(this);}在 setWindowManager 方法中token 被赋值到 Window 的 mAppToken 属性上同时在当前 Window 上创建了 WindowManager 。
继续看 WindowManagerImpl 的 createLocalWindowManager其中把当前 Window 作为参数传入
WindowManagerImpl.java
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}private WindowManagerImpl(Context context, Window parentWindow,Nullable IBinder windowContextToken) {mContext context;mParentWindow parentWindow; // 新建的 PhoneWindow 赋值给了 mParentWindowmWindowContextToken windowContextToken;
}从以上可知 WindowManagerImpl 的 mParentWindow 参数非空并且值是新建的 PhoneWindow。
这个非常重要在下面窗口添加的时候会判断这个变量然后来决定 token 的赋值操作。
五、通过添加窗口 Token 传递到 WMS
Activity.java
void makeVisible() {if (!mWindowAdded) {ViewManager wm getWindowManager();wm.addView(mDecor, getWindow().getAttributes());mWindowAdded true;}mDecor.setVisibility(View.VISIBLE);}关注这个 WindowManager 是通过 getWindowManager 获得的。 Activity.java
public WindowManager getWindowManager() {return mWindowManager;}我们知道这个 mWindowManager 其实是 Window 的 WindowManager Window.java mWindowManager ((WindowManagerImpl)wm).createLocalWindowManager(this);也就是说这个 WindowManagerImpl 的 mParentWindow 不为空并且就是之前 new 的 PhoneWindow。
接下来继续看窗口的添加代码
添加窗口的操作在 WindowManagerGlobal 的 addView 方法中如下 WindowManagerGlobal.java public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {if (view null) {throw new IllegalArgumentException(view must not be null);}if (display null) {throw new IllegalArgumentException(display must not be null);}if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException(Params must be WindowManager.LayoutParams);}final WindowManager.LayoutParams wparams (WindowManager.LayoutParams) params;if (parentWindow ! null) { // parentWindow 不为空parentWindow.adjustLayoutParamsForSubWindow(wparams);} else {// If theres no parent, then hardware acceleration for this view is// set from the applications hardware acceleration setting.final Context context view.getContext();if (context ! null (context.getApplicationInfo().flags ApplicationInfo.FLAG_HARDWARE_ACCELERATED) ! 0) {wparams.flags | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;}}......
}由前面的 parentWindow 赋值情况我们知道对于 Activity 启动流程来说走到这里parentWindow一定是不为 null 的。
其实只有系统窗口parentWindow 才会为 null。
以上可知 parentWindow 不为空然后走 Window 的 adjustLayoutParamsForSubWindow 函数确定 token值。
Window.java
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {CharSequence curTitle wp.getTitle();if (wp.type WindowManager.LayoutParams.FIRST_SUB_WINDOW wp.type WindowManager.LayoutParams.LAST_SUB_WINDOW) {if (wp.token null) {View decor peekDecorView();if (decor ! null) {wp.token decor.getWindowToken();// 如果是子窗口,则把父窗口的token赋值}}......} else if (wp.type WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW wp.type WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {......} else {if (wp.token null) {wp.token mContainer null ? mAppToken : mContainer.mAppToken;// 把当前窗口的 mAppToken 赋值给 wp.token}......}if (wp.packageName null) {wp.packageName mContext.getPackageName();}if (mHardwareAccelerated ||(mWindowAttributes.flags FLAG_HARDWARE_ACCELERATED) ! 0) {wp.flags | FLAG_HARDWARE_ACCELERATED;}}这里会判断窗口类型设置 token。获取到 Token 后就保存在了 LayoutParams 里面可知这个 token 来自窗口的 mAppToken。
之后被传递到 ViewRootImpl.setView 中去。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {synchronized (this) {if (mView null) {...mWindowAttributes.copyFrom(attrs);// 拷贝含有 token 的 attrs 到 mWindowAttributes...res mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls);// 传递 mWindowAttributes 到 WMS }}这里将包含 token 的 LayoutParams 通过 Session 最终调用到了 WMS 的 addWindow 方法这些流程前面的章节都提到过所以这里就简单带过。
WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsState requestedVisibility,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {...// 通过 token 获取到 DisplayContent来判断是否是非法的显示内容final DisplayContent displayContent getDisplayContentOrCreate(displayId, attrs.token);......//根据 token 获取 activity判断所添加的合法性WindowToken token displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);// 通过 displayContent 从 mTokenMap 中获取对应的 WindowToken第一次获取为空if (token null) {......final IBinder binder attrs.token ! null ? attrs.token : client.asBinder();token new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).build();// 通过这个 token 创建一个新的 WindowToken// 并会被添加到displayContent的mTokenMap 中......}......final WindowState win new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);// 然后以 WindowToken 创建 WindowState }综上简单总结一下每个 Activity 都有一个自己的 token用于各种校验而对于 WMS 来说如果想添加非系统级别的窗口都需要一个合理的 token。
接下来我们来分析下其他类型窗口的创建比如 Dialog。未完待续。。。。。。