Take the Dialog
for example.
Judge from the Android source code, let’s watch what happened when you called method Dialog#show()
method. The code can be simplified as below:
public void show() {
if (mShowing) {
// ...
return;
}
// ...
mDecor = mWindow.getDecorView();
mWindowManager.addView(mDecor, l);
// ...
mShowing = true;
// ...
}
The mDecor
field was got from mWidnow
, the mWindow
field was got from Dialog
's construcor:
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
// ...
final Window w = new PhoneWindow(mContext);
mWindow = w;
// ...
}
So that means one Dialog
has one mWindow, one mWindow has one mDecor.
When the mWindowManager.addView(mDecor, l)
was called, it went to the WindowManagerGlobal#addView()
.
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ...
synchronized (mLock) {
// ...
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
// EXCEPTION HERE!!!
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
// ...
}
}
The WindowManagerGlobal was singleton, so that means if you add the same view to WindowManagerGlobal twice, it will crash.
We can reprocedure this exception:
val dlg = AlertDialog.Builder(context).create()
dlg.setView(View.inflate(context, R.layout.layout_dialog_title_sample, null))
dlg.show()
val f = Dialog::class.java.getDeclaredField("mShowing")
f.isAccessible = true
f.setBoolean(dlg, false)
dlg.show()
Above, we change the the field mShowing
after the Dialog#show()
was called to show the dialog ‘twice’. And finally it throws the exception:
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ main, me.shouheng.suix.SampleApp$customCrash$1.onCrash(SampleApp.kt:64)
├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
│ ************* uncaught exception *************
│ Time Of Crash : 2020-01-10 14-46-43
│ Device Manufacturer: OnePlus
│ Device Model : ONEPLUS A6000
│ Android Version : 9
│ Android SDK : 28
│ App VersionName : 1.0
│ App VersionCode : 1
│
│ java.lang.IllegalStateException: View DecorView@3a84c11[MainActivity] has already been added to the window manager.
│ at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:328)
│ at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
│ at android.app.Dialog.show(Dialog.java:329)
│ at me.shouheng.suix.MainActivity$doCreateView$7.onClick(MainActivity.kt:72)
│ at android.view.View.performClick(View.java:6669)
│ at android.view.View.performClickInternal(View.java:6638)
│ at android.view.View.access$3100(View.java:789)
│ at android.view.View$PerformClick.run(View.java:26145)
│ at android.os.Handler.handleCallback(Handler.java:873)
│ at android.os.Handler.dispatchMessage(Handler.java:99)
│ at android.os.Looper.loop(Looper.java:193)
│ at android.app.ActivityThread.main(ActivityThread.java:6898)
│ at java.lang.reflect.Method.invoke(Native Method)
│ at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
│ at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────
So the final resolution of solving this problem was to check if you showed dialog twice exceptially in different thread.