前言
我们一直都被告知不能使用
非Activity外的Context
去启动一个dialog
,那到底是为什么呢?抱着这个疑问,我从源码里面找到了答案
分析
我们先写一个反面教材
//这样写会闪退
Dialog dialog = new Dialog(Application.getInstance);
dialog.show();
一、错误信息
根据
Log
我们可以知道,错误是从ViewRootImpl
的setView
里面发出的,我们开始一步一步定位错误
二、定位错误
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
requestLayout();
...
try {
...
//这是一次IPC
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
}
...
//错误就在这里
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?");
...
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
...
}
}
}
可以看到我们的错误是在:
res < WindowManagerGlobal.ADD_OKAY
case WindowManagerGlobal.ADD_BAD_APP_TOKEN || case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN
这两个条件同时满足时出现的。
首先
ADD_OKAY
是定义在WindowManagerGlobal
中的,除此之外还有其它的常量,而res
是从WMS
那里被赋值的(mWindowSession.addToDisplay
最终调用了WMS.addWindow
,这个可以通过XRef
查源码,这里不再赘述)。根据这里的判断逻辑,也就是说我们只要在WMS.addWindow
里面找到这样的代码块:return出以下定义中除ADD_OKAY外的任意一个值
,就能定位到具体错误所在地方
public static final int ADD_OKAY = 0;
public static final int ADD_BAD_APP_TOKEN = -1;
public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
public static final int ADD_NOT_APP_TOKEN = -3;
public static final int ADD_APP_EXITING = -4;
public static final int ADD_DUPLICATE_ADD = -5;
public static final int ADD_STARTING_NOT_NEEDED = -6;
public static final int ADD_MULTIPLE_SINGLETON = -7;
public static final int ADD_PERMISSION_DENIED = -8;
public static final int ADD_INVALID_DISPLAY = -9;
public static final int ADD_INVALID_TYPE = -10;
然后进入
WMS.addWindow
我们可以发现每个被WMS判断为不合法的情况,下面都会带有一句Slog.w(TAG_WM...)
。因此我们可以通过运行触发错误,然后将Log
等级设置为warn以上
,就可以看到会有这样一句Log
:
(注:这里的tag
是WindowManager
,因为我从Xref
找到关于TAG_WM
的定义如下第二张图)
有了具体的错误
Log
我们就能在WMS
里面直接搜索了,在WMS.addWindow
中通过Log
定位到代码位置如下:
public int addWindow(Session session, IWindow client,