- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我的应用程序显示一个 AlertDialog
,里面有一个 ListView
。一切正常,然后我决定测试它是否有内存泄漏。运行应用程序一段时间后,我打开了 MAT并生成 Leak Suspects 报告。 MAT 发现了几个类似的漏洞:
One instance of "com.android.internal.app.AlertController$RecycleListView" loaded by "<system class loader>" occupies ...
我花了很多时间寻找这次泄漏的原因。代码审查对我没有帮助,我开始谷歌搜索。这就是我发现的:
Issue 5054: AlertDialog seems to cause a memory leak through a Message in the MessageQueue
我决定检查这个错误是否重现。为此,我创建了一个包含两个 Activity 的小程序。 MainActivity
是一个输入点。它只包含一个运行 LeakedActivity
的按钮。后者只是在其 onCreate()
方法中显示一个 AlertDialog
。这是代码:
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);
}
}
每次关闭 AlertDialog
和 LeakedActivity
时,MAT 都会报告此应用程序泄漏 com.android.internal.app.AlertController$RecycleListView
完成。
我在这个小程序中找不到任何错误。它看起来像是使用 AlertDialog
的一个非常简单的案例,它必须运行良好,但似乎并没有。所以我想知道在对项目使用 AlertDialog
时如何避免内存泄漏。为什么这个问题还没有被修复?提前致谢。
最佳答案
(2/12/2012):请参阅下面的更新。
这个问题实际上不是由AlertDialog
引起的,而是与ListView
有关。您可以使用以下 Activity 重现相同的问题:
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"};
}
多次旋转设备,您将获得 OOM。
我没有时间进一步调查真正的原因(我知道发生了什么但不清楚为什么发生;可能是错误,或者设计)。但这里有一个您可以执行的解决方法,至少可以避免您的情况出现 OOM。
首先,您需要保留对泄漏的 AlertDialog
的引用。您可以在 onCreateDialog()
中执行此操作。当您使用 setItems()
时,AlertDialog
将在内部创建一个 ListView
。当您在 setItems()
调用中设置 onClickListener()
时,它会在内部分配给 ListView
onItemClickListener()
。
然后,在泄漏 Activity 的onDestroy()
中,设置AlertDialog
的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);
}
}
更新(2/12/2012): 经过进一步调查,这个问题实际上与 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
}
});
}
}
旋转几次,您将得到 OOM。问题是在你旋转之后,junk
仍然被保留,因为 GC 还没有也不会发生(如果你使用 MAT,你会看到这个 junk
仍然由按钮的监听器保留在 GCroot 深处,GC 需要时间来决定这个 junk
是否符合条件并且可以被 GC。)但与此同时,一个新的 junk
需要在旋转后创建,并且由于mem alloc 大小(每个垃圾10M),这会导致OOM。
解决方案是中断对监听器(垃圾
所有者)的任何引用,在本例中是从按钮中断,这实际上使监听器成为 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();
}
关于Android: AlertDialog 导致内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7083441/
我正在尝试在 AlertDialog 中创建一个 AlertDialog,但是当我运行代码时,没有出现第二个 AlertDialog 这是我的代码,我想让它像如果用户在第一个 AlertDialog
相关问题是here 制作android应用,我觉得我的代码不酷。 因为,每当需要对话框时我都会创建新的 AlderDialog.Builder 以防止出现此错误 “指定的子项已有父项。您必须先对子项的
我正在使用以下代码来创建警报对话框。 AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(thi
我正在尝试在警报对话框中添加一个警报对话框。但是看不到第二个警报对话框。请帮助我这是我显示的代码 AlertDialog alertDialog = new AlertDialog.Builder(m
为什么要使用 AlertDialog.Builder 类而不是 AlertDialog 直接可用的方法,例如,为什么使用 AlertDialog.Builder.setCancellable 而不是
假设您有一个带有两个按钮 A 和 B 的 AlertDialog。我发现在某些设备和某些版本的 android 上,如果您触摸对话框周围屏幕的任何区域,AlertDialog 消失。在其他设备上,您被
我目前正在使用需要使用大量 AlertDialogs 的应用程序。我目前在这里编写了一个基本的代码: protected void StopButton () { AlertDialog.Bu
我正在开发一个 Android 应用程序,并且我有一个 AlertDialog 子类。我想在对话框标题区域的右侧放置 2 个 ImageButtons(类似于 Activity 中的 ActionBa
我一直在尝试在 AlertDialog 中制作一个按钮Flutter 中的盒子。但我找不到拉伸(stretch)按钮容器的方法。请检查我的代码并查看下面的示例图片。 AlertDialog(
我正在尝试找出创建对话框的最佳方式。我可以创建自己的 Dialog 类(对我来说,它更干净、更有条理),或者我可以使用 AlertDialog.Builder(可以内联完成,而且看起来很时髦)……两者
我使用 AlertDialog.builder 创建了一个对话框,其中显示了可以检查的多选项目列表。 我设置了初始的项目名称集及其检查状态: builder.setMultiChoiceItems(
应用拦截短信并显示消息的对话框。 但是我无法在我的 Test 类中解决我的 Dialog 错误。我做错了什么? (我还包含了我的其他 2 个文件)。 Eclipse 中显示错误:AlertDialog
我的 flutter 应用程序中出现了一个 Flutter AlertDiaog。使用 Flutter Driver,我无法点击 Flutter AlertDialog 或 AlertDialog 上
我是 Jetpack compose 的初学者。现在在我的应用程序屏幕中,AlertDialog 用于向用户显示一些信息。 根据文档,当用户在对话框外或后退按钮上单击时,将调用 onDismissRe
我有一个 AlertDialog 的自定义子类,它应该显示范围内所有可用 Wifi 网络的列表。 我通过创建该对话框的实例并调用 show() 来显示此对话框,并且我没有使用 AlertDialog.
我有一个 AlertDialog 的子类,它应该显示范围内所有可用 Wifi 网络的列表。 我希望对话框本身负责启动 Wifi 扫描并接收结果。 出于这个原因,我不能使用 AlertDialog.Bu
标题几乎说明了一切。我需要关于如何做到这一点的建议。我可能只是将外部适配器添加为 AlertDialog 上的 View,以使其不那么复杂,但我仍然不知道如何与 交互来自内部适配器的 AlertDia
我想自定义 V7 AlertDialog 的 TITLE 颜色。 SetCustomTitle() 似乎无法与 android.support.v7.app.AlertDialog 一起使用。我可以看
创建 AlertDialog 然后显示和显示 AlertDialog.Builder 本身之间的主要区别是什么? 例如。我可以有一个像这样的 AlertDialog.Builder: AlertDi
关于我们什么时候应该使用 android.app.AlertDialog,或者我们什么时候应该使用 android.support.v7.app.AlertDialog,是否有任何指南? 因为,如果我
我是一名优秀的程序员,十分优秀!