美文网首页
Dialog引起的内存泄漏

Dialog引起的内存泄漏

作者: leenpong | 来源:发表于2018-12-27 20:05 被阅读0次

    前言:

    说到内存泄漏,有一个经常遇到过的就是非静态匿名内部类,编译的时候会持有外部的一个引用,如果如果该引用被用到一个比它自己生命周期长的地方,就可能造成泄露,比如非静态的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内存泄漏详解及其解决方案

    相关文章

      网友评论

          本文标题:Dialog引起的内存泄漏

          本文链接:https://www.haomeiwen.com/subject/ynydlqtx.html