C#, Mono for Android. I need to output a large portion of combined data into ListView. To achieve this, I use the following obvious approach with Adapter:
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 ());
}
}
This works very fine… until I start to refresh my ListView frequently. If I generate many ItemInfo’s (by refreshing my view, for example), I reach GREF limit soon (described here, “Unexpected NullReferenceExceptions” section), and my application crashes. Looking into Android log, I can see thousands of Android.Runtime.IJavaObject objects, which overflow GREF limit.
According to concepts of Mono VM + Dalvik VM bridge I can understand, that my ItemInfo’s need to be passed to Dalvik VM, wrapped to IJavaObject and to be formatted in ListView by native environment – this creates GREF’s. As long as garbage collecting is a non-determined process, if I call FillList() many times, old, already used ItemInfo’s stay into memory, leaking.
How can I avoid leaking? Or, possible, is there another way to output large portions of formatted data into ListView? I tried:
- I can’t reduce the number of ItemInfo’s, as long as I need to place my data somehow.
- I can’t follow this advice, as long as my ItemInfo is not an IJavaObject.
- As a temporarily solution, I call GC.Collect() every time I need to refresh list, but this looks not a clean way. Also, if I need to output more than 2к objects into list, this doesn’t help.
Answer found. I’ve inherited my ItemInfo class from Java.Lang.Object explicitly and now can call Dispose() when object is no longer needed. This kills leaking GREFs.