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

时间:2011-09-29 20:39:57

标签: android mono xamarin.android

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中?我试过了:

  • 我不能减少ItemInfo的数量,只要我需要以某种方式放置我的数据。
  • 只要我的ItemInfo不是IJavaObject,我就无法遵循此advice
  • 作为临时解决方案,我每次需要刷新列表时都会调用GC.Collect(),但这看起来并不干净。此外,如果我需要将超过2к个对象输出到列表中,这没有帮助。

2 个答案:

答案 0 :(得分:1)

在我的项目中,我遇到了同样的问题。

  1. ItemInfo是托管对象,因此您无需对其进行任何操作,GC会在必要时收集。
  2. ListView不会一次加载每个列表项的视图,因此您可以控制创建的视图的数量并进行处理。
  3. 这是我的解决方案

    我已从 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泄漏。