最近测试同学反馈一个问题:一个弹窗展示出来之后,点击按钮消失了。然后回到登录页再切回来时,这个弹窗又展示出来了。看到具体的代码:
1 | public class HintDialog extends DialogFragment { |
然后看到 JCommonDialog createDialog 方法:
1 | fun createDialog(context: Context, title: String, btnText: String, listener: View.OnClickListener): Dialog { |
isRobot 为 true 时,便会出现那个问题,如果是 false,使用 AlterDialog 便不会有那个问题。JCommonDialog 只是使用了一个自定义的布局,包含通用的标题、描述、左边按钮、右边按钮等元素,支持设置各种参数:
1 | class JCommonDialog(context: Context, private val params: DialogParams) : Dialog(context, R.style.dialog){ |
场景中,autoDismiss 为 true,所以点击按钮后会调 dismiss 方法。从实际情况来看,也确实消失了。只不过在 Activity 重新 onResume 的时候,它又展示出来了。因为是继承自 DialogFragment,怀疑是虽然调了 dialog dismiss 方法,但是这个 fragment 还在。通过如下代码打印相关日志:
1 | val fragment = supportFragmentManager.findFragmentByTag("HintDialog") |
得到的结果是:
true,false,false
而如果使用的是 AlertDialog,结果是:
null,null,null
通过日志印证了猜测,因为场景中并没有用到 DialogFragment 的任何特性,于是直接改成了正常的 Dialog。修改之后问题便没有再出现了。但仍然有个问题困扰着我:都是使用 DialogFragment 的方式,为什么 JCommonDialog 会有问题,而系统的 AlertDialog 则不会有问题呢?
起初怀疑 AlterDialog dismiss 有自己的逻辑,但是看到的源码中并没有覆写 dismiss 方法,最终调用的都是 Dialog 的 dismiss 方法,将 JCommonDialog 改成继承自 AlertDialog 问题依然存在。有没有可能是 style 引起的?可惜去掉 style 问题依然存在。然后看到 AlertDialog 的其他源码,使用的都是 AlertController 类,看到源码也没啥特别的逻辑:
1 | private final View.OnClickListener mButtonHandler = new View.OnClickListener() { |
点击一个按钮后,会执行相应的逻辑,然后立马就发一个 dismiss 的 message,执行的也是 Dialog 自身的 dismiss 方法,问题应该不在 AlterDialog。于是尝试屏蔽 JCommonDialog 的代码,不停尝试。最终发现:去掉 setOnDismissListener 之后问题便不出现了。于是怀疑是这个代码导致的,给 AlterDialog 也设置了 setOnDismissListener 对比测试一下,结果弹窗消失了,但压根没回调 onDismiss。突然转念一想,这不是用的 DialogFragment 嘛,为什么一直停留在 AlertDialog 上去寻找问题呢?于是看了看 DialogFragment 代码,一下子就清晰了:
1 | private void prepareDialog( { Bundle savedInstanceState) |
DialogFragment 中的 prepareDialog 方法,会将创建出来的 Dialog 的 setCancelable、setOnCancelListener、setOnDismissListener 改成 DialogFragment 自己的 listener:
1 | private DialogInterface.OnCancelListener mOnCancelListener = |
使用 AlertDialog 的方式,因为是在 Builder 里调用 setOnDismissListener,这个监听在 DialogFragment 创建出 Dialog 之后,马上就进行覆盖了。而使用 JCommonDialog 的方式,是在 Dialog 的 onCreate 方法进行设置的,而这个方法很显然是在 DialogFragment 设置监听之后。也就是 DialogFragment 设置的监听,后面被 Dialog onCreate 方法里自己设置的监听给覆盖掉了,导致DialogFragment.this.onDismiss(mDialog)
压根没有得到调用,那么 Fragment 相关的逻辑自然就丢失了。这也印证了前面去掉 setOnDismissListener 问题便不再出现了。
因为解决问题的思路没搞对,所以排查起来浪费了不少时间,值得记录一下。后续使用 DialogFragment 时,一定要注意 setCancelable、setOnCancelListener、setOnDismissListener 几个方法的使用!