Android : DialogFragment 内存泄露

问题描述

DialogFragment 内存泄露

问题出现的环境背景及自己尝试过哪些方法

clipboard.png

请问如何修复次泄露?

阅读 10.9k
4 个回答

解决方案:重写onActivityCreated,把这三个Listener都设置为空即可

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    dialog.setOnShowListener(null)
    dialog.setOnCancelListener(null)
    dialog.setOnDismissListener(null)
}

希望采纳,谢谢!

问题分析:
DialogFragment中有一个onActivityCreated方法,Android默认此方法实现是:

public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (this.mShowsDialog) {
            View view = this.getView();
            if (view != null) {
                if (view.getParent() != null) {
                    throw new IllegalStateException("DialogFragment can not be attached to a container view");
                }

                this.mDialog.setContentView(view);
            }

            Activity activity = this.getActivity();
            if (activity != null) {
                this.mDialog.setOwnerActivity(activity);
            }

            this.mDialog.setCancelable(this.mCancelable);
            this.mDialog.setOnCancelListener(this);
            this.mDialog.setOnDismissListener(this);
            if (savedInstanceState != null) {
                Bundle dialogState = savedInstanceState.getBundle("android:savedDialogState");
                if (dialogState != null) {
                    this.mDialog.onRestoreInstanceState(dialogState);
                }
            }

        }
    }

代码中会默认设置setOnCancelListener和setOnDismissListener

this.mDialog.setOnCancelListener(this);
this.mDialog.setOnDismissListener(this);

用setOnDismissListener分析一下,setOnCancelListener同理:

// Dialog 全局变量
private Message mDismissMessage;

// 此方法的实现是在Dialog类中,mDismissMessage是一个全局变量;
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) { // 当listener不为空时,会生成一个Message
            mDismissMessage = mListenersHandler.obtainMessage(DISMISS, listener);
        } else {
            mDismissMessage = null;
        }
    }

mListenersHandler的定义:

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;
            }
        }
    }

ListenersHandler主要是当处理Listener时,通过Handler来调用Listener的实现。

调用位置:
弹窗关闭的时候

@Override
    public void dismiss() {
        if (Looper.myLooper() == mHandler.getLooper()) {
            dismissDialog(); // 调用关闭弹窗
        } else {
            mHandler.post(mDismissAction);
        }
    }
    
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(); // 发送关闭弹窗的消息
        }
    }
    
    private void sendDismissMessage() {
        if (mDismissMessage != null) {
            // Obtain a new message so this dialog can be re-used
            Message.obtain(mDismissMessage).sendToTarget(); // 复制了这个消息并且发送到目标的Handler上,这里的Handler就是mListenersHandler(ListenersHandler)
        }
    }

因为mDismissMessage是全局变量,obj引用了Listener(DialogFragment),Dialog引用mDismissMessage,相互持有引用,存在引用数,导致内存泄漏。

分析的如有问题请纠正,谢谢。

参考:https://www.cnblogs.com/endur...

从这个图来来看,根源还是在你哥Message的obj引用,另外一个引用都是WeakReference的,你有用啥Handler发消息吗?

新手上路,请多包涵

@Coolspan 兄弟这样的使用是错误的,如果设置为空的话,显示过后的Dialog在Activity回到后台然后重新进入后,所有的Dialog会重新显示!

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏