Android Dialog Tips

Dialog 使用的常见坑.

  1. dismiss 错误堆栈
1
2
3
4
5
java.lang.IllegalArgumentException View=DecorView@984ba6d[MainActivity] not attached to window manager
android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:619)
android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:511)
android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:200)
android.app.Dialog.dismissDialog(Dialog.java:766)

这里查看堆栈是 DIalog dismiss 的堆栈, 但是一般看不到调用 dialog.dismiss 的业务堆栈.

主要是因为这种情况下一般是子线程调用了 dismiss, 但是 Dialog 的 dismiss 会有一个切线程的操作:

1
2
3
4
5
6
7
8
@Override
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
}

其中 mHandler 对象是在 Dialog 创建的时候初始化的, 所以 Looper 也是和创建 Dialog 的时候的线程绑定的, 同时因为 Handler 是 private 的, 所以目前想到的 hook 方式是自己同步创建一个 Handler , 在调用 dismiss 时自己切线程, 这样就可以正确 try catch 这个 Exception.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyDialog(context: Context): Dialog(context) {

private val handler = Handler()

override fun dismiss() {
if (Looper.myLooper() == handler.looper) {
superDismiss()
} else {
handler.post { superDismiss() }
}
}

private fun superDismiss() {
try {
super.dismiss()
} catch (e: Exception) {
// catch success
}
}
}