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