C#,Mono for Android。我需要将大部分组合数据输出到ListView中。为实现这一点,我使用以下明显的方法与适配器:
class ItemInfo
{
public string Id;
public string Name;
public string Description;
public int Distance;
//Some more data
}
class ItemAdapter : ArrayAdapter<ItemInfo>
{
public WorldItemAdapter (Context context, int textViewResourceId, List<WorldItemInfo> items) :
base(context, textViewResourceId, items)
{
}
//...
public override View GetView (int position, View convertView, ViewGroup parent)
{
//Some stuff to format ListViewItem
}
}
public class OutputActivity : Activity
{
ListView _listView;
//...
void FillList (object SomeParameters)
{
var adaptedList = someDataSource.Where().Join().Union().//anything can be imagined
.Select ((item, item2, item3) =>
new ItemInfo (){
Id = item.Id,
Name = item.Name,
Description = String.Format(..., item2, item3),
Distance = ...,
//so on
}
).OrderBy ((arg) => arg.Name).ToList ();
_listView.Adapter = new ItemAdapter (this, Resource.Layout.ListItemFormat, adaptedList ());
}
}
这非常好......直到我开始频繁刷新ListView。如果我生成了许多ItemInfo(例如通过刷新我的视图),我很快就会达到GREF限制(描述here,“Unexpected NullReferenceExceptions”部分),并且我的应用程序崩溃了。查看Android日志,我可以看到数千个Android.Runtime.IJavaObject对象,它们溢出GREF限制。
根据Mono VM + Dalvik VM桥的concepts,我可以理解,我的ItemInfo需要传递给Dalvik VM,包装到IJavaObject并由原生环境在ListView中格式化 - 这就创建了GREF。只要垃圾收集是一个非确定的过程,如果我多次调用FillList(),旧的,已经使用过的ItemInfo会留在内存中,泄漏。
如何避免泄漏?或者,是否有另一种方法可以将大部分格式化数据输出到ListView中?我试过了:
答案 0 :(得分:1)
在我的项目中,我遇到了同样的问题。
这是我的解决方案
我已从 BaseAdapter 中删除了一些不必要的覆盖,因此如果它要求您实施它们,请不要害怕。
class CustomViewAdapter : BaseAdapter<ItemInfo>
{
private readonly Context _context;
private IList<ItemInfo> _items;
private readonly IList<View> _views = new List<View>();
public CustomViewAdapter(IntPtr handle)
: base(handle)
{
}
public CustomViewAdapter (Context context, IList<ItemInfo> objects)
{
_context = context;
_items = objects;
}
private void ClearViews()
{
foreach (var view in _views)
{
view.Dispose();
}
_views.Clear();
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var inflater = LayoutInflater.From(_context);
var row = convertView ?? inflater.Inflate(Resource.Layout.ListItemView, parent, false);
/// set view data
if(!_views.Contains(row))
_views.Add(row);
return row;
}
public override int Count
{
get { return _items == null ? 0 : _items.Count; }
}
public override void Dispose()
{
ClearViews();
base.Dispose();
}
}
使用示例
[Activity]
public class MessagesActivity : Activity
{
private CustomViewAdapter _adapter;
protected override void OnCreate(Android.OS.Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.ListView);
_adapter=new CustomViewAdapter(this,Enumerable.Empty<ItemInfo>);
FindViewById<ListView>(Resource.Id.list).Adapter=_adapter;
}
public override void Finish()
{
base.Finish();
if(_adapter!=null)
_adapter.Dispose();
_adapter=null;
}
}
答案 1 :(得分:0)
找到答案。我明确地从Java.Lang.Object继承了我的ItemInfo类,现在可以在不再需要object时调用Dispose()。这会导致GREF泄漏。