Dialog表示と消去プロセス分析
45090 ワード
本明細書で参照するコードは、Android 5.0(API 22)バージョンです.
DialogクラスはDialogInterface,Windowを実現した.Callback, KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallbackという5つのインタフェース.よく使われる3つのインタフェース:Window.Callbackは受信画面touchイベントである.KeyEvent.Callbackはキーボードキーイベントとエンティティキーメッセージを受信する.Window.OnWindowDismissedCallbackは、受信ウィンドウのコールバックを消去します.
この文書では、Dialogの作成、非表示、表示、削除手順から、関連するコードフローを分析します.最後にいくつかのよく見られる異常分析を補充した.
ブロガーの労働成果を尊重し、転載して出典を明記してください.
作成
構築方法コードを見てみましょう.
PolicyManager#makeNewWindow関連コード:パス:/sources/android-22/com/android/internal/policy/PolicyManager.java
sPolicyがPolicyクラスのオブジェクト:パス:/sources/android-22/com/android/internal/policy/impl/Policy.java
WindowのsetWindowManagerメソッド関連コード:
表示
showメソッドコード:
mDecorはDecorViewオブジェクトで、DecorViewはFrameLayoutを継承します.Android 6.0以下のバージョンでは、DecorViewはPhoneWindowの内部クラスです.6.0以上のバージョンでは、抽出され、独立したクラスとなります.
Dialogを使用してfindViewメソッドとsetContentViewメソッドを呼び出していますが、実際にはPhoneWindowのメソッドが対応する操作を実行しています.Dialogの関連メソッドのコード:
PhoneWindowの関連コードを見てみましょう.
ここではDecorViewがどのように作成されたのかを詳しく説明しません.興味のあるのはソースコードを見たり、このブログを見たりすることができます.AndroidアプリケーションsetContentViewとLayoutInflaterのロード解析メカニズムのソースコード分析を見たりすることができます.
mWindowManagerを見てみましょうaddView(mDecor,l)の操作.このセクションの作成の解析から、mWindowManagerはWindowMangerImplのオブジェクトであることがわかります.まず、WindowMangerImplコードについて説明します.
WindowManagerGobalのaddViewメソッドコードを見続けます.
ViewRootImplがスクリーンイベントを受信および転送する方法については、Activity touchイベント転送プロセス分析を参照してください.setContentとshowを経験すると、きれいなDialogが携帯電話に表示されます.
隠して消える
呼び出しを隠すのはhideメソッドです.dialogが現在のページで頻繁に呼び出される場合は、この方法を使用します.DialogのルートビューmDecorは現在から削除されず、表示されないように設定されています.hideメソッドコード
コードdismissを呼び出し、backキーをクリックしたり、dialogの外部領域をクリックしたりすると、dialogを非表示にすることができます.
この方法で最も重要な操作はmWindowManagerです.removeViewImmediate(mDecor)では、Dialogを現在のビューから直接削除し、破棄して解放します.したがって、ページに頻繁に表示されるdialogはdimissよりもhideを採用する方が効率的である.
WindowManagerGlobalコードを見続けます.
以上の3つのステップの解析から,Dialogがどのように作成され,表示され,消去されたかが明らかになった.
補足:一般的な例外分析
仕事中に発生する可能性のあるいくつかの異常について、原因を分析します.
WindowLeaked
例外情報:
これはActivityがdestroyを実行している間にdialogがdismiss操作を実行されずに投げ出されたものである.onPause()またはonStop()でdismissを呼び出してdialogウィンドウを解放する必要があります.
ログに表示されているクラスを検索すると、めまいがすると思います.実際には、Activity ThreadがhandleDestroyActivity操作を実行するときに投げ出されます.コードに表示されたログが続く場合、この例外はonDestroyのログの後ろに表示されます.関連コード:
IllegalArgumentException
この異常はdismissを呼び出したときに発生し,WindowLeakedの後によく現れる.長時間の非同期操作の後、Activityはfinishリリースを回収または呼び出し、メインスレッドがdismissを呼び出すとこの例外が発生する可能性があります.主に、ネットワーク要求が終了してロードボックスを閉じるときに表示されます.解決方法:dismissの前にisFinishing()を使用して、現在のActivityがfinishされているかどうかを判断したり、onStop()またはonPaus()メソッドでdismissを呼び出したりして、onStartメソッドで表示する必要があるかどうかを判断します.例外情報:
異常なコードを投げ出すのはこの節のWindowManagerGlobalのfindView Locked方法の中にあります.mDecorは削除され、dialogはクローズメッセージを受信していないため、dialogはmDecorが現在のビューにあると判断したため、再びremoveがエラーを報告した.
BadTokenException
この例外はnewを使用してDialogを作成するのはActivityオブジェクトに転送されていないのではなく、他のContextです.token検証が失敗し、異常が放出されました.システム権限がある場合は、他のContextで作成できます.
ViewRootImpl.setViewのコード:
DialogクラスはDialogInterface,Windowを実現した.Callback, KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallbackという5つのインタフェース.よく使われる3つのインタフェース:Window.Callbackは受信画面touchイベントである.KeyEvent.Callbackはキーボードキーイベントとエンティティキーメッセージを受信する.Window.OnWindowDismissedCallbackは、受信ウィンドウのコールバックを消去します.
この文書では、Dialogの作成、非表示、表示、削除手順から、関連するコードフローを分析します.最後にいくつかのよく見られる異常分析を補充した.
ブロガーの労働成果を尊重し、転載して出典を明記してください.
作成
構築方法コードを見てみましょう.
//
public Dialog(Context context) {
this(context, 0, true);
}
//
public Dialog(Context context, int theme) {
this(context, theme, true);
}
//
Dialog(Context context, int theme, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (theme == 0) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, outValue, true);
theme = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, theme);
} else {
mContext = context;
}
// WindowManagerImpl , Activity windowManger
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
// PhoneWindow
Window w = PolicyManager.makeNewWindow(mContext);
// ,mWindow PhoneWindow , WindowManger
mWindow = w;
Window.Callback
w.setCallback(this);
//window dismiss
w.setOnWindowDismissedCallback(this);
// Window setWindowManager PhoneWindow WindowMangerImpl
w.setWindowManager(mWindowManager, null, null);
//
w.setGravity(Gravity.CENTER);
// Handler
mListenersHandler = new ListenersHandler(this);
}
PolicyManager#makeNewWindow関連コード:パス:/sources/android-22/com/android/internal/policy/PolicyManager.java
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
sPolicyがPolicyクラスのオブジェクト:パス:/sources/android-22/com/android/internal/policy/impl/Policy.java
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
WindowのsetWindowManagerメソッド関連コード:
public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
setWindowManager(wm, appToken, appName, false);
}
public 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);
}
表示
showメソッドコード:
public void show() {
if (mShowing) {//dialog show , dismiss false
if (mDecor != null) {
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
// Dialog View, Dialog
mDecor.setVisibility(View.VISIBLE);
}
return;
}
mCanceled = false;
if (!mCreated) {
dispatchOnCreate(null);
}
onStart();
// DecorView
mDecor = mWindow.getDecorView();
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
final ApplicationInfo info = mContext.getApplicationInfo();
mWindow.setDefaultIcon(info.icon);
mWindow.setDefaultLogo(info.logo);
mActionBar = new WindowDecorActionBar(this);
}
//
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
nl.copyFrom(l);
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
l = nl;
}
try {
// , dialog
mWindowManager.addView(mDecor, l);
mShowing = true;
// dialog
sendShowMessage();
} finally {
}
}
mDecorはDecorViewオブジェクトで、DecorViewはFrameLayoutを継承します.Android 6.0以下のバージョンでは、DecorViewはPhoneWindowの内部クラスです.6.0以上のバージョンでは、抽出され、独立したクラスとなります.
Dialogを使用してfindViewメソッドとsetContentViewメソッドを呼び出していますが、実際にはPhoneWindowのメソッドが対応する操作を実行しています.Dialogの関連メソッドのコード:
public View findViewById(int id) {
return mWindow.findViewById(id);
}
public void setContentView(int layoutResID) {
mWindow.setContentView(layoutResID);
}
public void setContentView(View view) {
mWindow.setContentView(view);
}
public void setContentView(View view, ViewGroup.LayoutParams params) {
mWindow.setContentView(view, params);
}
public void addContentView(View view, ViewGroup.LayoutParams params) {
mWindow.addContentView(view, params);
}
PhoneWindowの関連コードを見てみましょう.
// DecorView
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
// ,
installDecor();
}
return mDecor;
}
// DecorView
private void installDecor() {
if (mDecor == null) {// null,
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
...//
}
// DecorView
protected DecorView generateDecor() {
// DecorView,-1 id, xml
return new DecorView(getContext(), -1);
}
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
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);
}
// Dialog mWindow ,cb Dialog
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// TODO Augment the scenes/transitions API to support this.
Log.v(TAG, "addContentView does not support content transitions");
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
ここではDecorViewがどのように作成されたのかを詳しく説明しません.興味のあるのはソースコードを見たり、このブログを見たりすることができます.AndroidアプリケーションsetContentViewとLayoutInflaterのロード解析メカニズムのソースコード分析を見たりすることができます.
mWindowManagerを見てみましょうaddView(mDecor,l)の操作.このセクションの作成の解析から、mWindowManagerはWindowMangerImplのオブジェクトであることがわかります.まず、WindowMangerImplコードについて説明します.
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
//
private final Display mDisplay;
private final Window mParentWindow;
private IBinder mDefaultToken;
...//
// View
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
// Only use the default token if we don't have a parent window.
if (mDefaultToken != null && mParentWindow == null) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
// Only use the default token if we don't already have a token.
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (wparams.token == null) {
wparams.token = mDefaultToken;
}
}
}
...//
// View, dimiss
public void removeViewImmediate(View view) {
//true
mGlobal.removeView(view, true);
}
...//
}
WindowManagerGobalのaddViewメソッドコードを見続けます.
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...//
//ViewRootImpl , DecorView,
// (true false) 。
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
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);
}
// do this last because it fires off messages to start doing things
try {
// DecorView ViewRootView, ,dialog
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
ViewRootImplがスクリーンイベントを受信および転送する方法については、Activity touchイベント転送プロセス分析を参照してください.setContentとshowを経験すると、きれいなDialogが携帯電話に表示されます.
隠して消える
呼び出しを隠すのはhideメソッドです.dialogが現在のページで頻繁に呼び出される場合は、この方法を使用します.DialogのルートビューmDecorは現在から削除されず、表示されないように設定されています.hideメソッドコード
public void hide() {
if (mDecor != null) {
mDecor.setVisibility(View.GONE);
}
}
コードdismissを呼び出し、backキーをクリックしたり、dialogの外部領域をクリックしたりすると、dialogを非表示にすることができます.
//
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
// ,
dismissDialog();
} else {
// , , mListenersHandler
mHandler.post(mDismissAction);
}
}
// dismiss
void dismissDialog() {
// mDecor null dismiss ,
if (mDecor == null || !mShowing) {
return;
}
// PhoneWindow ,
if (mWindow.isDestroyed()) {
Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
return;
}
try {
// mDecor, mWindowManager WindowManagerGlobal
mWindowManager.removeViewImmediate(mDecor);
} finally {
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;// null,
mWindow.closeAllPanels();
onStop();
mShowing = false;// false
// dismiss
sendDismissMessage();
}
}
この方法で最も重要な操作はmWindowManagerです.removeViewImmediate(mDecor)では、Dialogを現在のビューから直接削除し、破棄して解放します.したがって、ページに頻繁に表示されるdialogはdimissよりもhideを採用する方が効率的である.
WindowManagerGlobalコードを見続けます.
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
// View
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
// ViewRootImpl
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
// View ,
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
//
private int findViewLocked(View view, boolean required) {
final int index = mViews.indexOf(view);
if (required && index < 0) {
// View ,
throw new IllegalArgumentException("View=" + view + " not attached to window manager");
}
return index;
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {// View
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
//ViewRootImpl die
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
以上の3つのステップの解析から,Dialogがどのように作成され,表示され,消去されたかが明らかになった.
補足:一般的な例外分析
仕事中に発生する可能性のあるいくつかの異常について、原因を分析します.
WindowLeaked
例外情報:
android.view.WindowLeaked: Activity xx.xxActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{3e7fbc08 V.E..... R.....I. 0,0-960,231} that was originally added here
at android.view.ViewRootImpl.(ViewRootImpl.java:462)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:278)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.Dialog.show(Dialog.java:311)
これはActivityがdestroyを実行している間にdialogがdismiss操作を実行されずに投げ出されたものである.onPause()またはonStop()でdismissを呼び出してdialogウィンドウを解放する必要があります.
ログに表示されているクラスを検索すると、めまいがすると思います.実際には、Activity ThreadがhandleDestroyActivity操作を実行するときに投げ出されます.コードに表示されたログが続く場合、この例外はonDestroyのログの後ろに表示されます.関連コード:
//ActivityThread handleDestroyActivity
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
cleanUpPendingRemoveWindows(r);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
if (r.activity.mVisibleFromServer) {
mNumVisibleActivities--;
}
// Activity window token,
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
if (r.onlyLocalRequest) {
// Hold off on removing this until the new activity's
// window is being added.
r.mPendingRemoveWindow = v;
r.mPendingRemoveWindowManager = wm;
} else {
wm.removeViewImmediate(v);
}
}
if (wtoken != null && r.mPendingRemoveWindow == null) {
// wtoken DecorView
WindowManagerGlobal.getInstance().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
}
r.activity.mDecor = null;
}
if (r.mPendingRemoveWindow == null) {
// If we are delaying the removal of the activity window, then
// we can't clean up all windows here. Note that we can't do
// so later either, which means any windows that aren't closed
// by the app will leak. Well we try to warning them a lot
// about leaking windows, because that is a bug, so if they are
// using this recreate facility then they get to live with leaks.
// token DecorView
WindowManagerGlobal.getInstance().closeAll(token,
r.activity.getClass().getName(), "Activity");
}
…//
}
…//
}
//WindowManagerGlobal closeAll
public void closeAll(IBinder token, String who, String what) {
synchronized (mLock) {
int count = mViews.size();
//Log.i("foo", "Closing all windows of " + token);
// DecorView, token
for (int i = 0; i < count; i++) {
//Log.i("foo", "@ " + i + " token " + mParams[i].token
// + " view " + mRoots[i].getView());
// token null, DecorView token
if (token == null || mParams.get(i).token == token) {
ViewRootImpl root = mRoots.get(i);
//Log.i("foo", "Force closing " + root);
// who ‘Activity’
if (who != null) {
// ,
WindowLeaked leak = new WindowLeaked(
what + " " + who + " has leaked window "
+ root.getView() + " that was originally added here");
leak.setStackTrace(root.getLocation().getStackTrace());
Log.e(TAG, "", leak);
}
//
removeViewLocked(i, false);
}
}
}
}
IllegalArgumentException
この異常はdismissを呼び出したときに発生し,WindowLeakedの後によく現れる.長時間の非同期操作の後、Activityはfinishリリースを回収または呼び出し、メインスレッドがdismissを呼び出すとこの例外が発生する可能性があります.主に、ネットワーク要求が終了してロードボックスを閉じるときに表示されます.解決方法:dismissの前にisFinishing()を使用して、現在のActivityがfinishされているかどうかを判断したり、onStop()またはonPaus()メソッドでdismissを呼び出したりして、onStartメソッドで表示する必要があるかどうかを判断します.例外情報:
java.lang.IllegalArgumentException: View=com.android.internal.policy.impl.PhoneWindow$DecorView{3e7fbc08 V.E..... R.....I. 0,0-960,231} not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:416)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:342)
at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:116)
at android.app.Dialog.dismissDialog(Dialog.java:354)
at android.app.Dialog.dismiss(Dialog.java:337)
異常なコードを投げ出すのはこの節のWindowManagerGlobalのfindView Locked方法の中にあります.mDecorは削除され、dialogはクローズメッセージを受信していないため、dialogはmDecorが現在のビューにあると判断したため、再びremoveがエラーを報告した.
BadTokenException
この例外はnewを使用してDialogを作成するのはActivityオブジェクトに転送されていないのではなく、他のContextです.token検証が失敗し、異常が放出されました.システム権限がある場合は、他のContextで作成できます.
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:685)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:289)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:85)
at android.app.Dialog.show(Dialog.java:311)
ViewRootImpl.setViewのコード:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
…//
int res; /* = WindowManagerImpl.ADD_OKAY; */
…//
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// token
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
} catch (RemoteException e) {
…//
} finally {
…//
}
…//
// token
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?");
…//
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
…//
}
}
}