Mic*_*ael 14 android memory-leaks android-alertdialog
我的应用程序显示了AlertDialog一个ListView内部.一切都工作得很好然后我决定测试这个内存泄漏.运行应用程序一段时间后,我打开了MAT并生成了Leak Suspects报告.MAT发现了几个类似的泄漏:
由"<system class loader>"加载的"com.android.internal.app.AlertController $ RecycleListView"的一个实例占用...
我花了很多时间寻找泄漏的原因.代码审查没有帮助我,我开始谷歌搜索.这就是我发现的:
问题5054:AlertDialog似乎通过MessageQueue中的Message导致内存泄漏
我决定检查这个bug是否重现.为此我创建了一个由两个活动组成的小程序.MainActivity是一个enrty点.它只包含一个运行的按钮LeakedActivity.后者只是AlertDialog在其onCreate()方法中显示了一个.这是代码:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.button).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
new Intent(MainActivity.this, LeakedActivity.class));
}
});
}
}
public class LeakedActivity extends Activity {
private static final int DIALOG_LEAK = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
showDialog(DIALOG_LEAK);
}
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == DIALOG_LEAK) {
return new AlertDialog.Builder(this)
.setTitle("Title")
.setItems(new CharSequence[] { "1", "2" },
new OnClickListener() {
private final byte[] junk = new byte[10*1024*1024];
@Override
public void onClick(DialogInterface dialog, int which) {
// nothing
}
})
.create();
}
return super.onCreateDialog(id);
}
}
Run Code Online (Sandbox Code Playgroud)
MAT报告此应用程序com.android.internal.app.AlertController$RecycleListView每次AlertDialog被解雇并LeakedActivity完成时都会泄漏.
我在这个小程序中找不到任何错误.它看起来像一个非常简单的使用情况,AlertDialog它必须运作良好,但似乎没有.因此,我想知道如何在使用AlertDialog带项目的s 时避免内存泄漏.为什么这个问题没有修复呢?提前致谢.
Ric*_*Lee 27
(2012年12月12日):见下面的更新.
这个问题实际上并不是由AlertDialog与之相关的问题引起的ListView.您可以使用以下活动重现相同的问题:
public class LeakedListActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Use an existing ListAdapter that will map an array
// of strings to TextViews
setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, mStrings));
getListView().setOnItemClickListener(new OnItemClickListener() {
private final byte[] junk = new byte[10*1024*1024];
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
}
});
}
private String[] mStrings = new String[] {"1", "2"};
}
Run Code Online (Sandbox Code Playgroud)
旋转设备几次,你就会得到OOM.
我没有时间去研究更多关于真正原因的事情(我知道发生了什么但不清楚它为什么会发生;可能是bug,或者是设计的).但是这是你可以做的一种解决方法,至少在你的情况下避免使用OOM.
首先,您需要保留对泄露的参考AlertDialog.你可以在这里做到这一点onCreateDialog().当你使用时setItems(),AlertDialog遗嘱将在内部创建一个ListView.当您onClickListener()在setItems()通话中设置时,它将在内部分配给ListView onItemClickListener().
然后,在泄漏的活动中onDestroy(),将AlertDialog's ListView' 设置onItemClickListener()为null,这将释放对侦听器的引用,使得在该侦听器内分配的任何内存都符合GC的条件.这样你就不会得到OOM.这只是一种解决方法,真正的解决方案应该实际应用于ListView.
以下是您的示例代码onDestroy():
@Override
protected void onDestroy() {
super.onDestroy();
if(leakedDialog != null) {
ListView lv = leakedDialog.getListView();
if(lv != null) lv.setOnItemClickListener(null);
}
}
Run Code Online (Sandbox Code Playgroud)
UPDATE(2012/2/12):经过进一步调查,这个问题其实并没有特别涉及到ListView也不是OnItemClickListener,但这样的事实,GC不会立即发生,需要时间来决定哪些对象有资格和准备GC.试试这个:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// this will create reference from button to
// the listener which in turn will create the "junk"
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
private byte[] junk = new byte[10*1024*1024];
@Override
public void onClick(View v) {
// do nothing
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
旋转几次,你就会得到OOM.问题是你旋转之后junk仍然保留,因为GC还没有,也不会发生(如果你使用MAT,你会看到这junk仍然是由GCroot深处的按钮的监听器保留的,它会需要时间让GC确定这junk是否合格并且可以GCed.)但同时,junk需要在旋转后创建一个新的,并且由于mem alloc大小(每个垃圾10M),这将导致OOM .
解决方案是打破对侦听器(junk所有者)的任何引用,在这种情况下是从按钮,这实际上使侦听器成为GCroot,只有短路径到垃圾,并使GC决定更快地回收垃圾内存.这可以在以下方面完成onDestroy():
@Override
protected void onDestroy() {
// this will break the reference from the button
// to the listener (the "junk" owner)
findViewById(R.id.button).setOnClickListener(null);
super.onDestroy();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11653 次 |
| 最近记录: |