dialog.setOnDismissListener(null)过程分析

前提

为解决DialogFragment的内存泄漏,使用了此篇博客的处理方法 DialogFragment 内存泄露,简单说就是给 dialog 设置

getDialog().setOnCancelListener(null);
getDialog().setOnDismissListener(null);

但发现了一个问题,当用户返回Activity时,会再次显示对话框!!

之前也有人反馈:

后来调试发现不能设置此监听

getDialog().setOnDismissListener(null);

究竟源码里面做了什么操作,导致会再次显示呢???
带着这个问题来看一下源码


过程分析

setOnDismissListener
    /**
     * Set a listener to be invoked when the dialog is dismissed.
     * @param listener The {@link DialogInterface.OnDismissListener} to use.
     */
    public void setOnDismissListener(@Nullable OnDismissListener listener) {
        if (mCancelAndDismissTaken != null) {
            throw new IllegalStateException(
                    "OnDismissListener is already taken by "
                    + mCancelAndDismissTaken + " and can not be replaced.");
        }
        if (listener != null) {
            mDismissMessage = mListenersHandler.obtainMessage(DISMISS, listener);
        } else {
            mDismissMessage = null;
        }
    }

可以看出 设置与不设置监听的差别在于是否有 mDismissMessage的存在,mDismissMessage起到了什么样的作用?

检查发现它在此方法会使用:

    private void sendDismissMessage() {
        if (mDismissMessage != null) {
            // Obtain a new message so this dialog can be re-used
            Message.obtain(mDismissMessage).sendToTarget();
        }
    }

sendDismissMessage

此方法会发送一个消息,这个消息就是设置监听时定义的那个消息,系统给我们的标识是DISMISS。即消失dialog时如果设置了此监听,就会发送。

系统是默认创建时就设置了此监听:

dismissDialog

继续跟踪会发现,sendDismissMessage();会在dismissDialog()里调用,

    void dismissDialog() {
        if (mDecor == null || !mShowing) {
            return;
        }

        if (mWindow.isDestroyed()) {
            Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
            return;
        }

        try {
            mWindowManager.removeViewImmediate(mDecor);
        } finally {
            if (mActionMode != null) {
                mActionMode.finish();
            }
            mDecor = null;
            mWindow.closeAllPanels();
            onStop();
            mShowing = false;

            sendDismissMessage();
        }
    }

dismissDialog()一定会在dialog消失的时候调用:

Dialog.java

片段一:

    @Override
    public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog();
        } else {
            mHandler.post(mDismissAction);
        }
    }

片段二:

    private final Runnable mDismissAction = this::dismissDialog;

所以现在是知道了,在dialog结束时,系统会发送DISMISS消息来做一些事情,如果将监听设置为null,则系统就不会处理那些事情!

((OnDismissListener) msg.obj).onDismiss(mDialog.get());

我们找到处理消息的地方,Dialog.java

private static final class ListenersHandler extends Handler {
        private final WeakReference<DialogInterface> mDialog;

        public ListenersHandler(Dialog dialog) {
            mDialog = new WeakReference<>(dialog);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DISMISS:
                    ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
                    break;
                case CANCEL:
                    ((OnCancelListener) msg.obj).onCancel(mDialog.get());
                    break;
                case SHOW:
                    ((OnShowListener) msg.obj).onShow(mDialog.get());
                    break;
            }
        }
    }

继续看 onDismiss 做了哪些处理:

onDismiss

进入父类 PreferenceDialogFragment.java:

    @Override
    public void onDismiss(DialogInterface dialog) {
    	// 点击
        super.onDismiss(dialog);
        onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
    }

点击后到达 DialogFragment.java:

    public void onDismiss(DialogInterface dialog) {
        if (!mViewDestroyed) {
            // Note: we need to use allowStateLoss, because the dialog
            // dispatches this asynchronously so we can receive the call
            // after the activity is paused.  Worst case, when the user comes
            // back to the activity they see the dialog again.
            dismissInternal(true);
        }
    }

通过注释我们可以(通过有道翻译)知道:

注意:我们需要使用allowStateLoss,因为对话框异步地分派这个调用,这样我们就可以在activity paused后接收调用。最坏的情况是,当用户返回到activity时,他们再次看到对话框。

mViewDestroyed 初始化的时候被设置为 false,所以默认是触发 dismissInternal(true); 这个方法的

    @Override
    public void onStart() {
        super.onStart();
        if (mDialog != null) {
            mViewDestroyed = false;
            mDialog.show();
        }
    }

void dismissInternal(boolean allowStateLoss)

    void dismissInternal(boolean allowStateLoss) {
        if (mDismissed) {
            return;
        }
        mDismissed = true;
        mShownByMe = false;
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
        }
        mViewDestroyed = true;
        if (mBackStackId >= 0) {
            getFragmentManager().popBackStack(mBackStackId,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            mBackStackId = -1;
        } else {
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.remove(this);
            if (allowStateLoss) {
                ft.commitAllowingStateLoss();
            } else {
                ft.commit();
            }
        }
    }

显示的时候:

        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();

dialog消失的时候:

            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.remove(this);
            if (allowStateLoss) {
                ft.commitAllowingStateLoss();
            } else {
                ft.commit();
            }

总结

如果设置了 dialog.setOnDismissListener(null)那么 点击空白区的时候(不调用dismiss()),不会执行 dismissInternal(true),从而FragmentTransaction 不会remove这个dialogfragment;而注释写的很明白,当用户返回到activity时,会再次看到对话框。

如果点击按钮,执行了dismiss(),则不会再出现对话框!!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以在弹窗的"确定"按钮的点击事件中,将"ide[0]"的值更新为当前选中项的索引。然后在弹窗的"onDismiss"方法中,判断"ide[0]"的值是否发生了变化,如果没有变化,说明用户没有做出选择,可以弹出提示框提示用户进行选择,否则继续向下执行。代码如下所示: ``` final int[] ide = {-1}; // 初始化为-1,表示用户还没有做出选择 unit_kind = "科室"; unit_xingzhi = ""; String[] keys = kezhang_names.keySet().toArray(new String[0]); View view = LayoutInflater.from(LuzhiAdmin_Xiangmu_Xiao0.this).inflate(R.layout.tanchuang, null); AlertDialog.Builder builder = new AlertDialog.Builder(LuzhiAdmin_Xiangmu_Xiao0.this); builder.setView(view); builder.setTitle("确定人员:"); builder.setSingleChoiceItems(keys, 0, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ide[0] = which; return; } }).setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (ide[0] == -1) { // 如果用户没有做出选择,弹出提示框 Toast.makeText(LuzhiAdmin_Xiangmu_Xiao0.this, "请先选择人员", Toast.LENGTH_SHORT).show(); return; } id_perx = "#" + kezhang_names.get(keys[ide[0]]) + "#"; } }); AlertDialog dialog = builder.create(); Window window = dialog.getWindow(); if (window != null) { WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); layoutParams.width = 600; layoutParams.height = 800; window.setAttributes(layoutParams); } dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialogInterface) { if (ide[0] == -1) { // 如果用户没有做出选择,弹出提示框 Toast.makeText(LuzhiAdmin_Xiangmu_Xiao0.this, "请先选择人员", Toast.LENGTH_SHORT).show(); return; } // 继续向下执行 } }); dialog.show(); ``` 这样,弹窗就会等待用户做出选择后才能继续向下执行。如果用户没有做出选择,会弹出提示框提醒用户。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值