Android Monodroid-根据GREF限制将数据传递到ListView

Android Monodroid-根据GREF限制将数据传递到ListView,android,mono,xamarin.android,Android,Mono,Xamarin.android,C、 Android的Mono。我需要将大部分组合数据输出到ListView中。为了实现这一点,我对适配器使用了以下明显的方法: class ItemInfo { public string Id; public string Name; public string Description; public int Distance; //Some more data } class ItemAdapter : ArrayAdapter<Item

C、 Android的Mono。我需要将大部分组合数据输出到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限制,意外的NullReferenceExceptions部分,我的应用程序崩溃。查看Android日志,我可以看到数千个Android.Runtime.IJavaObject对象,它们超出了GREF限制

根据Mono VM+Dalvik VM bridge的定义,我可以理解,我的ItemInfo需要传递给Dalvik VM,包装到IJavaObject,并由本机环境在ListView中格式化-这会创建GREF。只要垃圾收集是一个不确定的过程,如果我多次调用FillList,旧的、已经使用的ItemInfo就会留在内存中,发生泄漏

我怎样才能避免泄漏?或者,有没有其他方法可以将大部分格式化数据输出到ListView?我试过:

我不能减少ItemInfo的数量,只要我需要以某种方式放置数据。 只要我的ItemInfo不是IJavaObject,我就无法理解这一点。 作为一种临时解决方案,每次需要刷新列表时,我都会调用GC.Collect,但这看起来不是一种干净的方法。另外,若我需要将2个以上的对象输出到列表中,这也并没有帮助。
在我的项目中,我也遇到了同样的问题

ItemInfo是托管对象,所以您不需要对它做任何事情,GC会在必要时收集它。 ListView不会一次加载每个列表项的视图,所以您可以控制创建视图的数量并对其进行处理。 这是我的解决办法

我已经从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();
    }
} 
用法示例


找到答案。我从Java.Lang.Object显式继承了ItemInfo类,现在可以在不再需要该对象时调用Dispose。这将杀死泄漏的GREFs。

谢谢,这是一个非常好的概念,我将考虑在将来使用它,但这显然不是我的问题。我对视图没有任何问题——事实上,当活动完成时,它们应该由本机代码自动与ListView一起处理,所以这里不需要关心我。我需要以某种方式处理ItemInfo。使用延迟加载实体或像这样重用现有项,但使用ItemInfo如何。var行=转换视图??inflater.InflateResource.Layout.ListItemView,父级,false;找到了另一个解决方案。我从Java.Lang.Object显式继承了ItemInfo类,现在可以在不再需要该对象时调用Dispose。这会杀死泄漏的GREFs
[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;
    }
}