I’m developing an app for Android using mvvmcross.
In this application I want to have a list which contains a spinner. It looks ok when I test the app on the emulator, but when I scroll it goes out of memory quickly because the gref goes above 2000. I know the gref can go higher on a real device but I still think I must be doing something wrong.
BindableList
<cirrious.mvvmcross.binding.android.views.MvxBindableListView
android:id="@+id/propertyHolder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/obsBtLayout"
android:layout_above="@id/photoframe"
local:MvxBind="
{
'ItemsSource':{'Path':'PPHolders'},
'ItemClick':{'Path':'PropertyClickedCommand'}
}"
local:MvxItemTemplate="@layout/listitem_property"
/>
ListItem_Property.axml (stripped)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/AIPApp.UI.Droid"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/ListItemSelector"
android:descendantFocusability="beforeDescendants"
>
<cirrious.mvvmcross.binding.android.views.MvxBindableSpinner
android:layout_gravity="center_horizontal"
android:layout_width="200dip"
android:layout_height="wrap_content"
local:MvxDropDownItemTemplate="@layout/spinneritem_propdropdown"
local:MvxItemTemplate="@layout/spinneritem_prop"
local:MvxBind="
{
'ItemsSource':{'Path':'CodeTableValues'},
'SelectedItem':{'Path':'ObservedCodeTable'},
'Visibility':{'Path':'IsCodeTableValue','Converter':'Visibility'}
}"/>
</LinearLayout>
Is this happening because the spinner items have to be rebuild every time I scroll? Because the list it’s bound to is different in every item on the list. So on one listitem the spinner list can be six items long on another it can be 3 items long and so on.
I’ve not yet got to a full analysis of the behavior you are seeing – without a full sample of your code it is very hard to do.
However, thanks especially to JonPryor on the Xamarin forums I believe I do now at least have a better understanding of what is happening to GREFs in the general case – and so I can answer your ‘why’ question.
The general case for bound lists is that the GREFs are incremented:
TextViewand aButtonwithin each ListView item, then the C# will store references to these viewsIn your example, each list item will itself contain a bound list – and this will lead to multiplication of the number of GREFs required – which is why you are seeing the problems reported.
With this understanding in place, the obvious question might be ‘how can we solve this?’
That’s not a simple question to answer, but I think there are a few ways we might be able to tackle the problem.
Firstly, we could talk with Xamarin about this issue – it may be that the number of available GREFs should be increased – since these objects will be in memory in Java, then perhaps there is no harm with them being referenced in C# as well?
Secondly, we could look at changing the way our UI binding is implemented so that permanent references are not stored to all objects – e.g if we know a binding in one-time (e.g. for a label), then we might look at a route which does not use XML data-binding for this functionality. For example, we could use a new View to perform this binding manually.
Thirdly, we could look at changing the binding code itself so that for one-way binding (from ViewModel to View) it retrieves the Android Views using
FindViewById<TView>at update time instead of using retained references to the views. This would be slower, but would reduce the number of GREFs required. This functionality might be most achievable in the case of explicitly stated ‘one-time’ bindings.Fourthly, this is the solution which might be most accessible to you as an app developer, you could look at changing the UI implementation so that the app doesn’t use these bound sublists – e.g. it could instead use a label – which only creates the spinner on-demand (by handling the Click event in your code).
I’m sure there are other options beyond this too…
One question I asked myself during this analysis is whether this problem is unique to MvvmCross, or whether it is a problem which might be seen across all MonoDroid applications.
I’m not 100% sure, but the answer, I think, is that this a general issue which would affect all MonoDroid apps. However, I also think that MvvmCross has added a little to the problem:
I also don’t think this is entirely just an MvvmCross or MonoDroid problem. While this GREF limit is being highlighted because of MonoDroid’s implementation, the underlying issue here is really one of trying to do too much at one time – so really you could improve your app’s performance by stream-lining the design/implementation so that it uses less views. While it may not feel like it, I think MonoDroid is doing us a favour here – it’s pointing out that our UI implementation is a bit ‘fat’ and we should look at optimising it in our app code.
I will update this answer as I find out more, but I hope the above information already gives you quite a good insight into the ‘why’ of this situation.