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-usedMessage.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

片段一:

    @Overridepublic 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);}@Overridepublic 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:

    @Overridepublic 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); 这个方法的

    @Overridepublic 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(),则不会再出现对话框!!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部