Dialog引起的内存泄漏

2018-12-27  本文已影响0人  leenpong

前言:

说到内存泄漏,有一个经常遇到过的就是非静态匿名内部类,编译的时候会持有外部的一个引用,如果如果该引用被用到一个比它自己生命周期长的地方,就可能造成泄露,比如非静态的handler类。

但是有些地方比如

view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    
                }
            });

并不会造成内存泄漏,虽然OnClickListener持有外部引用,但是在当前view所在的界面的视图被销毁后,其view也被销毁,所以没问题

正文:

        AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
        builder.setTitle("Title")
                .setMessage("Message")
                .setNegativeButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                }).show();

上面出现匿名内部类的地方:

setNegativeButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }


        public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {
            P.mNegativeButtonText = text;
            P.mNegativeButtonListener = listener;
            return this;
        }

Dialog是通过AlertDialog.Builder组织参数,进一步看show方法。

        public AlertDialog show() {
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }


        public AlertDialog create() {
            // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
            // so we always have to re-set the theme
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            P.apply(dialog.mAlert);
            ....
}

public void apply(AlertController dialog) {
          ...
                      if (mPositiveButtonText != null || mPositiveButtonIcon != null) {
                dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                        mPositiveButtonListener, null, mPositiveButtonIcon);
            }
            if (mNegativeButtonText != null || mNegativeButtonIcon != null) {
                dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                        mNegativeButtonListener, null, mNegativeButtonIcon);
            }

          ...

}

    public void setButton(int whichButton, CharSequence text,
            DialogInterface.OnClickListener listener, Message msg, Drawable icon) {

        if (msg == null && listener != null) {
            msg = mHandler.obtainMessage(whichButton, listener);
        }

        switch (whichButton) {

            case DialogInterface.BUTTON_POSITIVE:
                mButtonPositiveText = text;
                mButtonPositiveMessage = msg;
                mButtonPositiveIcon = icon;
                break;

            case DialogInterface.BUTTON_NEGATIVE:
                mButtonNegativeText = text;
                mButtonNegativeMessage = msg;
                mButtonNegativeIcon = icon;
                break;

            case DialogInterface.BUTTON_NEUTRAL:
                mButtonNeutralText = text;
                mButtonNeutralMessage = msg;
                mButtonNeutralIcon = icon;
                break;

            default:
                throw new IllegalArgumentException("Button does not exist");
        }
    }

上面show的流程中到setbutton,可以知道,其匿名内部类DialogInterface.OnClickListener最终通过setButton,包装成一个本地成员变量mButtonPositiveMessage保存在dialog内部,而dialog在处理按钮的点击方法如下 :

        mButtonPositive.setOnClickListener(mButtonHandler);

 private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            final Message m;
            if (v == mButtonPositive && mButtonPositiveMessage != null) {
                m = Message.obtain(mButtonPositiveMessage);
            } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
                m = Message.obtain(mButtonNegativeMessage);
            } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
                m = Message.obtain(mButtonNeutralMessage);
            } else {
                m = null;
            }

            if (m != null) {
                m.sendToTarget();
            }

            // Post a message so we dismiss after the above handlers are executed
            mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialog)
                    .sendToTarget();
        }
    };

也就是说虽然是通过view的clickListener注册监听,但是内部是通过Message发送一条信息去处理该动作。

            if (m != null) {
                m.sendToTarget();
            }

内部的实现就是handler,这个时候,如果当前页面退出,但是发送的message被阻塞到messagequeue对列,就会导致message对象持有外部的引用而内存泄漏.

关于handler引起的内存泄漏和解决可参考:Handler内存泄漏详解及其解决方案

上一篇 下一篇

猜你喜欢

热点阅读