抛出的异常:

Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an applicationat android.view.ViewRootImpl.setView(ViewRootImpl.java:685)at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)at android.app.Dialog.show(Dialog.java:316)

抛出的地方:

Res 来自 windowSession,即来自 WindowManagerService:

可以看到,异常说 attr.token 不是一个 app 的 token,attr 是 setView 方法的参数,是一个 WindowManager.LayoutParams 对象,WindowManger.LayoutParams 继承了 ViewGroup.LayoutParams。

// WindowManagerGlobalpublic void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {    ...}

setView 被 addView 调用,传递了 wparams 给 attrs:

// WindowManagerGlobalpublic void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {    ...    root.setView(view, wparams, panelParentView);    ...}

如果 parentWindow 不为空,wparams 会使用 parentWindow 来赋值:

// WindowManagerGlobalpublic void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {    ...    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;    if (parentWindow != null) {        parentWindow.adjustLayoutParamsForSubWindow(wparams);    }    ...    root.setView(view, wparams, panelParentView);    ...}

parentWindow 中会根据 mContainer 的值来决定 wp.token 的值:

// Windowvoid adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {    ...    if (wp.token == null) {        wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;    }    ...}

mContainer 只会在 Activity 中赋值:

// Windowpublic void setContainer(Window container) {    mContainer = container;    ...}// Activityfinal void attach(...) {    ...    if (mParent != null) {        mWindow.setContainer(mParent.getWindow());    }    ...}

Activity 的 mParent 是 ActivityGroup,已经被废弃了,所以一定为 null,则 mContainer 一定为 null,所以 Window#adjustLayoutParamsForSubWindow 方法中 mp.token 为 mAppToken,即 WindowManagerGlobal#addView 方法中 parentWindow 参数的 mAppToken。

WindowManagerGlobal#addView 方法会被 WindowManagerImpl#addView 方法调用,parentWindow 的值为 mParentWindow。

// WindowManagerImplpublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {    applyDefaultToken(params);    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);}

mParentWindow 的赋值来自 createLocalWindowManger,createLocalWindowManger 会被 Window 的 setWindowManger 调用。

setWindowManger 调用时,会将自己作为 parentWindow,也就是调用 setWindowManger 的 Window 对象会被作为 parentWindow。

总结一下:

  • 一个 WindowMangerImpl 有一个 mParentWindow 成员。
  • 一个 Window 有一个 mWindowManager 成员。
  • 当 Window 通过 setWindowManger 方法生成 mWindowManager 时,这个 mWindowManager 会同时将 Window 作为 mParentWindow。
  • 所以,一个 WindowManger 的 token 如果不手动指定,则取决于它的 mParentWindow 对象(即生成它的 Window 对象)的 mAppToken。
// WindowMangerImplprivate WindowManagerImpl(Context context, Window parentWindow) {    mContext = context;    mParentWindow = parentWindow;}public WindowManagerImpl createLocalWindowManager(Window parentWindow) {    return new WindowManagerImpl(mContext, parentWindow);}// Windowpublic void setWindowManager(WindowManager wm, IBinder appToken, String appName,        boolean hardwareAccelerated) {    mAppToken = appToken;    mAppName = appName;    mHardwareAccelerated = hardwareAccelerated            || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);    if (wm == null) {        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);    }    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);}

Dialog 的构造函数中会生成 WindowManger:

// DialogDialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {    ...    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);    ...}

对 Activity 来说,返回的是自己的 mWindowManager

// Activitypublic 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 的 mWindowManger 来自它 PhoneWindow 的 WindowManger:

// Activityfinal void attach(...) {    ...    mWindowManager = mWindow.getWindowManager();    ...}

PhoneWindow 的 WindowManger 来自 setWindowManager 方法。

// Activityfinal void attach(...) {    ...    mWindow.setWindowManager(        (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),        mToken, mComponent.flattenToString(),        (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);    ...    mWindowManager = mWindow.getWindowManager();    ...}

根据上面的结论,PhoneWindow 的 WindowManager 的 token 取决于 PhoneWindow 的 mAppToken。

在上面的 setWindowManager 方法参数中可以看到,PhoneWindow 的 mAppToken 来自 Activity 的 mToken,所以它的 WindowManger 的 token 也就是 Activity 的 mToken。
如果是其他 Context,则没有这个 mToken。

总结一下:

  • Dialog 在构造时生成 WindowManger
    • 如果是 Activity,则获取到的是 Activity 的 WindowManger,它的 WindowManger.LayoutParams 的 token 是 Activity 的 token。
    • 如果是其他 Context,则获取到的是一个新的 WindowManger,它的 WindowManger.LayoutParams 的 token 是 null。
  • Dialog show 时
    • 如果 WindowManger.LayoutParams 的 token 不是 Activity 的 token,则抛出异常。

更多相关文章

  1. Android(安卓)TV listView焦点平滑移动
  2. Android(安卓)onMeasure、Measure、measureChild、measureChildr
  3. android inputreader 部分对event数据的处理
  4. 在Android(安卓)7.0上PopupWindow.showAsDropDown不起作用的解决
  5. android获取设备屏幕大小的方法
  6. Android实现手机震动抖动效果的方法
  7. Android级联菜单的实现方法
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. android使用全局变量的方法
  2. ANDROID ONTOUCHEVENT, ONCLICK及ONLONGC
  3. Android的简单组件总结
  4. 常用Android系统调用
  5. Android常用颜色值
  6. 2.4.4 进度条
  7. 【Android】使用Git控制Android程序的git
  8. 启动 flutter项目时报Could not find com
  9. Android自定义View之一:初探实例
  10. Android通过Uri获取文件的路径的方法