dialog token null is not valid; is your activity running源码分析
Posted 爱炒饭
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dialog token null is not valid; is your activity running源码分析相关的知识,希望对你有一定的参考价值。
大家直到在android中普通dialog(不是系统级dialog)构造函数的context必须是activity类型的,不能是application类型的context,为什么呢?本文从源码角度来分析下,分为androidX场景和非androidX场景。
下面是一个简单的普通dialog弹出,关键在于第一Builder的构造参数是application的context,毫无意外,运行会报错。
AlertDialog alertDialog = new AlertDialog.Builder(getApplicationContext())
.setMessage("haha")
.create();
alertDialog.show();
1、androidX场景
具体报错信息如下:
Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:846)
at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:809)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:696)
at androidx.appcompat.app.AppCompatDialog.setContentView(AppCompatDialog.java:95)
at androidx.appcompat.app.AlertController.installContent(AlertController.java:232)
at androidx.appcompat.app.AlertDialog.onCreate(AlertDialog.java:279)
at android.app.Dialog.dispatchOnCreate(Dialog.java:419)
at android.app.Dialog.show(Dialog.java:313)
at com.shan.unittestapp.MainActiv,ity2.onCreate(MainActivity2.java:44)
可以看到是由于androidX下必须要使用Theme.AppCompat主题才行,根据报错trace可以看出最终是在AppCompatDelegateImpl.java的createSubDecor方法中。
//AppCompatDelegateImpl.java
private ViewGroup createSubDecor()
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar))
a.recycle();
throw new IllegalStateException(
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
……
2、非androidX场景
具体报错信息如下,可以看到常见的“Unable to add window – token null is not valid; is your activity running?”,从报错信息也可以看出需要activity的token。
2021-12-06 20:40:53.146 1910-5502/? W/WindowManager: Attempted to add application window with unknown token null. Aborting.
2021-12-06 20:40:53.146 14522-14522/? D/AndroidRuntime: Shutting down VM
2021-12-06 20:40:53.146 14522-14522/? E/AndroidRuntime: FATAL EXCEPTION:main Process: com.shan.unittestapp, PID: 14522
java.lang.RuntimeException: Unable to start activity ComponentInfocom.shan.unittestapp/com.shan.unittestapp.MainActivity2:
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:798)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.app.Dialog.show(Dialog.java:329)
at com.shan.unittestapp.MainActivity2.onCreate(MainActivity2.java:152)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
现在从下面具体报错trace来追查问题出现根因,我们也可以从这个过程查看下dialog的显示流程
at android.view.ViewRootImpl.setView(ViewRootImpl.java:798)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.app.Dialog.show(Dialog.java:329)
2.1 Dialog构造函数
Dialog有三种构造函数,分别是一个参数构造函数、两个参数构造函数、三个参数构造函数,最终三个参数构造函数会被调用。可以看出Dialog也有自己的Window和WindowManager,这里需要注意下第4行的 context.getSystemService获取WindowManager,这也是context使用的地方。
//Dialog.java
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper)
……
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);
如果这里的context是activity类型的,那么getSystemService(Context.WINDOW_SERVICE)方法就会返回activity自身的mWindowManager。
2.1.1.1 Activity.getSystemService
//Activity.java
@Override
public Object getSystemService(@ServiceName @NonNull String name)
if (getBaseContext() == null)
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
if (WINDOW_SERVICE.equals(name))
return mWindowManager;
else if (SEARCH_SERVICE.equals(name))
ensureSearchManager();
return mSearchManager;
return super.getSystemService(name);
activity自身的mWindowManager是怎么来的呢?查看Activity.java可以看到是在Activity.attach方法中设置的,具体Activity.attach方法是从哪里调过来的,可以查看我的另一篇文章[从startActivity说起]。
(https://blog.csdn.net/u013795543/article/details/101125143)
2.1.1.2 Activity.attach
//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)
……
mWindow = new PhoneWindow(this, window, activityConfigCallback);
……
mToken = token;
……
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null)
mWindow.setContainer(mParent.getWindow());
mWindowManager = mWindow.getWindowManager();
……
可以看到在Activity.attach方法中先调用了Window的setWindowManager方法,在该方法中设置mWindow的管理类WindowManager,这里需要注意一下,setWindowManager有一个参数是IBinder 类型的token,而上面报错就提示了“Attempted to addapplication window with unknown token null. Aborting.”,那应该就和这里的token有关了。 然后通过mWindow.getWindowManager()再取出来复制给mWindowManager。具体看下mWindow.setWindowManager的实现。
2.1.1.3 Window.setWindowManager
//Window.java
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated)
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null)
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
在该方法中将参数token赋值给了mAppToken成员变量,并且通过WindowManagerImpl.createLocalWindowManager创建了一个WindowManagerImpl实例。这里需要提一下,WindowManager是一个接口,具体实现就是WindowManagerImpl。
2.1.1.4 WindowManagerImpl.createLocalWindowManager
//WindowManagerImpl.java
public WindowManagerImpl createLocalWindowManager(Window parentWindow)
return new WindowManagerImpl(mContext, parentWindow);
private WindowManagerImpl(Context context, Window parentWindow)
mContext = context;
mParentWindow = parentWindow;
createLocalWindowManager方法参数传过来了一个window,并且调用了WindowManagerImpl具有两个参数的构造函数,此时mParentWindow就不为null了,是有父窗口的。
如果这里的context是非activity类型的,startService过程一文曾经提到context的具体实现类是ContextImpl.java,那么getSystemService(Context.WINDOW_SERVICE)方法就会返回ContextImpl的mWindowManager。
2.1.2.1 ContextImpl.getSystemService
//ContextImpl.java
@Override
public Object getSystemService(String name)
return SystemServiceRegistry.getSystemService(this, name);
具体调用了SystemServiceRegistry的getSystemService方法,SystemServiceRegistry类似ServiceManager的功能,先注册再获取。
2.2.2 SystemServiceRegistry.registerService
//SystemServiceRegistry.java
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>()
@Override
public WindowManager createService(ContextImpl ctx)
return new WindowManagerImpl(ctx);
);
注册时会调用WindowManagerImpl一个参数构造函数。
2.1.2.3 WindowManagerImpl
public WindowManagerImpl(Context context)
this(context, null);
private WindowManagerImpl(Context context, Window parentWindow)
mContext = context;
mParentWindow = parentWindow;
WindowManagerImpl一个参数的构造函数会调用两个参数的构造函数,只是第二个参数为null,那么mParentWindow 就是null了。
2.2 Dialog.show
Dialog构造函数中讲了Dialog有自己的window,那么在Dialog.show中就根据mWindow的getDecorView方法获取到了mDecor ,然后调用mWindowManager(WindowManagerImpl)的addView方法。
//Dialog.java
public void show()
……
mDecor = mWindow.getDecorView();
……
mWindowManager.addView(mDecor, l);
……
2.3 WindowManagerImpl.addView
//WindowManagerImpl.java
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
WindowManagerImpl又把addView的动作交给了WindowManagerGlobal,这里的第三个参数就是mParentWindow,在2.1节有讲过当dialog的context是activity类型是mParentWindow不为null,否则为null。
2.4 WindowManagerGlobal.addView
//WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow)
……
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null)
parentWindow.adjustLayoutParamsForSubWindow(wparams);
else
// If there's no parent, then hardware acceleration for this view is
// set from the application's 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;
ViewRootImpl root;
View panelParentView = null;
……
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try
root.setView(view, wparams, panelParentView);
catch (RuntimeException e)
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0)
removeViewLocked(index, true);
throw e;
addView方法的第三个参数Window parentWindow比较重要,这也是activity类型的context和非activity的context的区别,如果activity类型的context则parentWindow不为空,那么代码就会走到 parentWindow.adjustLayoutParamsForSubWindow(wparams)里面。
//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();
if (curTitle == null || curTitle.length() == 0)
final StringBuilder title = new StringBuilder(32);
if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA)
title.append("Media");
else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY)
title.append("MediaOvr");
else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL)
title.append("Panel");
else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL)
title.append("SubPanel");
else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL)
title.append("AboveSubPanel");
else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG)
title.append("AtchDlg");
else
title.append(wp.type);
if (mAppName != null)
title.append(":").append(mAppName);
wp.setTitle(title);
else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW)
// We don't set the app token to this system window because the life cycles should be
// independent. If an app creates a system window and then the app goes to the stopped
// state, the system window should not be affected (can still show and receive input
// events).
if (curTitle == null || curTitle.length() == 0)
final StringBuilder title = new StringBuilder(32);
title.append("Sys").append(wp.type);
if (mAppName != null)
title.append(":").append(mAppName);
wp.setTitle(title);
else
if (wp.token == null)
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
if ((curTitle == null || curTitle.length() == 0)
&& mAppName != null)
wp.setTitle(mAppName);
……
Window .adjustLayoutParamsForSubWindow里面根据默认窗口类型去执行不同的操作,dialog默认窗口是应用级的,所以会走到最后一个else,所以最后这个WindowManager.LayoutParams的token就不为null了,后面会用到。
接着WindowManagerGlobal中创建了ViewRootImpl并且调用了ViewRootImpl.setView方法,并将WindowManager.LayoutParams类型的wparams传了过去。
2.5 ViewRootImpl.setView
开始的报错trace最终指出ViewRootImpl.setView报错了,看来越来越接近真相了,在ViewRootImpl.setView已经可以从res变量的值ADD_BAD_APP_TOKEN看到了开始的报错信息,所以问题就出在mWindowSession.addToDisplay添加失败了。
//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
synchronized (this)
……Attributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
……
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY)
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
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_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
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_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
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);
if (view instanceof RootViewSurfaceTaker)
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
……
2.5.1 Session.addToDisplay
//Session.java
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);
Session.addToDisplay调用了WindowManagerService.java(WMS)的addWindow方法。
2.5.2 WindowManagerService.addWindow
// WindowManagerService.java
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)
……
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null)
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW)
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate())
Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
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;
if (rootType == 以上是关于dialog token null is not valid; is your activity running源码分析的主要内容,如果未能解决你的问题,请参考以下文章
dialog token null is not valid; is your activity running源码分析
dialog token null is not valid; is your activity running源码分析
Android之Dialog提示Unable to add window -- token is not valid; is your activity running?
Android之Dialog提示Unable to add window -- token is not valid; is your activity running?
可以启动 jquery 对话框,但是打开 get Uncaught TypeError: $(...).dialog is not a function
hadoopAccessControlException: SIMPLE authentication is not enabled. Available:[TOKEN, KERBEROS]