【发布时间】:2010-10-22 14:50:15
【问题描述】:
我正在努力解决与 ListView 相关的内存泄漏问题。我创建了以下展示这种行为的小程序。
我所做的是创建 2 个线性布局。第一个有一个 Button 和一个 GListView 控件。 GListView 的代码如下,但它只是 ListView 的子类,并实现了 ListAdapter 接口。创建 GListView 时,它会将其适配器设置为自身。
现在,当您按下按钮时,我切换到第二个 LinearLayout。此布局只有一个按钮。当您按下此按钮时,我会使用新的 GListView 创建一个新的第一个布局并将其设置为活动视图。
运行程序,在两个视图之间切换 20 次。然后启动 DDMS 并强制进行垃圾收集。然后转储内存,使用内存分析器,你会发现剩下 21 个 GListView 对象。也就是说,不再连接任何东西的 20 个 GListView 还没有被释放。
如果我进行内存转储并查看应该已回收的 GListView 之一,并使用内存分析器列出传入的引用,我会得到以下信息:
Class Name | Shallow Heap | Retained Heap
-------------------------------------------------------------------------------------------------------
com.gabysoft.memoryleak.GListView @ 0x43e72270 Unknown | 672 | 3,528
|- host android.view.View$ScrollabilityCache @ 0x43e72560 | 80 | 584
|- this$0 android.widget.AbsListView$RecycleBin @ 0x43e72830 | 40 | 160
|- mCallback android.graphics.drawable.StateListDrawable @ 0x43e728a8 | 64 | 1,464
|- this$0 android.widget.AdapterView$AdapterDataSetObserver @ 0x43e730b8| 16 | 16
-------------------------------------------------------------------------------------------------------
现在,如果我在 GListView 构造函数中注释掉 'setAdapter(this)' 函数并重复上述操作,我发现只剩下 1 个 GListView。也就是说,在这种情况下,所有未使用的 GListView 都已正确回收。
有人建议我在 GListView 中创建一个私有类来处理 ListAdapter 接口,我尝试过,但没有帮助。我也尝试过创建一个完全独立的公共类来处理 ListAdapter,但是,唉,这似乎也不起作用。
当这些对象不再在任何地方使用时,肯定有一些方法可以让它们消失。 (这不就是垃圾收集的全部内容吗?)
任何帮助将不胜感激。我真的在这个上拔了头发。
谢谢。
/*
* Activity
*/
package com.gabysoft.memoryleak;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
public class MemoryLeak extends Activity implements android.view.View.OnClickListener
{
LinearLayout ll2;
boolean page2 = false;
private LinearLayout CreateLayout()
{
LinearLayout ll = new LinearLayout(this);
Button btn1 = new Button(this);
ListView lv = new GListView(this);
btn1.setText("Press");
btn1.setLayoutParams(new LinearLayout.LayoutParams(100, 40));
btn1.setOnClickListener(this);
ll.addView(btn1);
ll.addView(lv);
return(ll);
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
CreateLayout();
LinearLayout ll = CreateLayout();
ll2 = new LinearLayout(this);
Button btn2 = new Button(this);
btn2.setText("Back");
btn2.setLayoutParams(new LinearLayout.LayoutParams(100, 40));
btn2.setOnClickListener(this);
ll2.addView(btn2);
setContentView(ll);
}
@Override
public void onClick(View v)
{
if (page2)
{
LinearLayout ll = CreateLayout();
setContentView(ll);
page2 = false;
}
else
{
setContentView(ll2);
page2 = true;
}
}
}
/*
* GListView
*/
package com.gabysoft.memoryleak;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class GListView extends ListView implements ListAdapter
{
Context m_context;
DataSetObserver m_observer = null;
public GListView(Context context)
{
super(context);
m_context = context;
setAdapter(this);
setChoiceMode(CHOICE_MODE_SINGLE);
}
/*
* ListAdapter
*/
@Override
public boolean areAllItemsEnabled()
{
return true;
}
@Override
public boolean isEnabled(int position)
{
return true;
}
@Override
public int getCount()
{
return(0);
}
@Override
public Object getItem(int position)
{
return null;
}
@Override
public long getItemId(int position)
{
return(position);
}
@Override
public int getItemViewType(int position)
{
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
TextView tv = new TextView(m_context);
tv.setText("Item");
return(tv);
}
@Override
public int getViewTypeCount()
{
return 1;
}
@Override
public boolean hasStableIds()
{
return false;
}
@Override
public boolean isEmpty()
{
return false;
}
@Override
public void registerDataSetObserver(DataSetObserver observer)
{
m_observer = observer;
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer)
{
m_observer = null;
}
}
【问题讨论】:
-
@John Gaby:发布一个 ZIP 文件的链接,该文件包含一个带有此代码的完整项目,我可以下载并使用它,我会看看它。
-
感谢您的浏览。我看不到如何将文件与此评论一起发布,因此我将其上传到我的网站。您可以在gabysoft.com/download/memoryleak.zip 下载它。我正在使用 Android 2.2 在模拟器上运行它(尽管我相信它也发生在真实设备上)
-
@John Gaby:是的,你不能将文件附加到 SO 问题中,所以你在那里做的很好。给我几个小时,因为我有新书更新,我想先出门。
-
谢谢,非常感谢您的帮助。我尝试了很多不同的东西,似乎无论我做什么,我都无法让我的 GListView 对象被回收。
-
另请注意,实际应用程序要复杂得多,并且做的事情与这个简单示例中的有所不同。我在这里尝试做的是创建一个尽可能简单的程序,以展示我在完整应用程序中看到的行为。
标签: android memory-leaks