Hi
We are currently building a Xamarin Forms application but have encountered a problem. It is sometimes crashing (in Android) whenever an ObservableCollection is updated from an async task. We do some rather heavy XML processing and network communication to produce/update the elements so running everything synchronously on the UI thread is a no-go.
The full error-log/some code reproducing the problem is presented below but I guess the meat of the problem is this
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.
So hmm, yep! This is exactly what we are doing and here is the question. Should the Xamarin Forms listview support such behaviour or are we developers expected to ensure that content of an observablecollection is always updated from the ui thread? If so, how is this done most easily?
Thank you very much in advance and have a great day.
Code reproducing the error:
`
public class App
{
public App()
{
this.DataSource = new ObservableCollection();
}
public ObservableCollection<ListItem> DataSource { get; private set; }
public int Count { get; private set; }
public void BumpCount()
{
++this.Count;
}
public static Page GetMainPage()
{
var app = new App();
var goButton = new Button()
{
Text = "Go",
Command = new Command(async () =>
{
await Task.Run(() =>
{
app.DataSource.Clear();
app.BumpCount();
for (int i = 0; i < 100; ++i)
{
app.DataSource.Add(new ListItem()
{
Line1 = string.Format("{0}: Line1 - {1}", app.Count, i),
Line2 = string.Format("{0}: Line2 - {1}", app.Count, i),
});
}
}).ConfigureAwait(false);
}),
};
var listView = new ListView()
{
ItemsSource = app.DataSource,
ItemTemplate = new DataTemplate(typeof(ListViewCell)),
};
var sl = new StackLayout()
{
Children =
{
goButton,
listView,
},
};
return new ContentPage
{
Content = sl,
};
}
}
public class ListItem
{
public string Line1 { get; set; }
public string Line2 { get; set; }
}
public class ListViewCell : ViewCell
{
public ListViewCell()
{
var sl = new StackLayout()
{
};
var l = new Label();
l.SetBinding(Label.TextProperty, "Line1");//, BindingMode.TwoWay);
sl.Children.Add(l);
var l2 = new Label();
l2.SetBinding(Label.TextProperty, "Line2");
sl.Children.Add(l2);
this.View = sl;
}
}`
Here follows the full exception description:
09-29 09:10:26.278 I/MonoDroid(23017): UNHANDLED EXCEPTION: Java.Lang.IllegalStateException: Exception of type 'Java.Lang.IllegalStateException' was thrown.
09-29 09:10:26.278 I/MonoDroid(23017): at Android.Runtime.JNIEnv.CallVoidMethod (intptr,intptr,Android.Runtime.JValue[]) [0x00063] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.14-series/a5d57087/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:507
09-29 09:10:26.278 I/MonoDroid(23017): at Android.Views.ViewGroup.Layout (int,int,int,int) [0x0002d] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.14-series/a5d57087/source/monodroid/src/Mono.Android/platforms/android-14/src/generated/Android.Views.ViewGroup.cs:2674
09-29 09:10:26.278 I/MonoDroid(23017): at Xamarin.Forms.Platform.Android.ViewRenderer
2<Xamarin.Forms.ListView, Android.Widget.ListView>.OnLayout (bool,int,int,int,int) <0x00223>09-29 09:10:26.278 I/MonoDroid(23017): at Xamarin.Forms.Platform.Android.ListViewRenderer.OnLayout (bool,int,int,int,int)
09-29 09:10:26.278 I/MonoDroid(23017): at Xamarin.Forms.Platform.Android.FormsViewGroup.n_OnLayout_ZIIII (intptr,intptr,bool,int,int,int,int)
09-29 09:10:26.278 I/MonoDroid(23017): at (wrapper dynamic-method) object.bd94aabe-665a-4f02-b0a6-551512d7fdbc (intptr,intptr,bool,int,int,int,int)
09-29 09:10:26.278 I/MonoDroid(23017):
09-29 09:10:26.278 I/MonoDroid(23017): --- End of managed exception stack trace ---
09-29 09:10:26.278 I/MonoDroid(23017): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(-1, class android.widget.ListView) with Adapter(class xamarin.forms.platform.android.ListViewAdapter)]
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.ListView.layoutChildren(ListView.java:1555)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.AbsListView.onLayout(AbsListView.java:2087)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.ListViewRenderer.n_onLayout(Native Method)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.ListViewRenderer.onLayout(ListViewRenderer.java:62)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at com.xamarin.forms.platform.android.FormsViewGroup.measureAndLayout(FormsViewGroup.java:29)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.VisualElementRenderer_1.n_onLayout(Native Method)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.VisualElementRenderer_1.onLayout(VisualElementRenderer_1.java:46)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at com.xamarin.forms.platform.android.FormsViewGroup.measureAndLayout(FormsViewGroup.java:29)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.VisualElementRenderer_1.n_onLayout(Native Method)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.VisualElementRenderer_1.onLayout(VisualElementRenderer_1.java:46)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at com.xamarin.forms.platform.android.FormsViewGroup.measureAndLayout(FormsViewGroup.java:29)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.PlatformRenderer.n_onLayout(Native Method)
09-29 09:10:26.278 I/MonoDroid(23017): at xamarin.forms.platform.android.PlatformRenderer.onLayout(PlatformRenderer.java:54)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1660)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.LinearLayout.onLayout(LinearLayout.java:1436)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:374)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.layout(View.java:14817)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.ViewGroup.layout(ViewGroup.java:4631)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
09-29 09:10:26.278 I/MonoDroid(23017): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
09-29 09:10:26.278 I/MonoDroid(23017): at android.view.View.
09-29 09:10:31.058 W/dalvikvm(23017): JNI WARNING: JNI function NewString called with exception pending
09-29 09:10:31.058 W/dalvikvm(23017): in Lxamarin/forms/platform/android/ListViewRenderer;.n_onLayout:(ZIIII)V (NewString)
09-29 09:10:31.058 W/dalvikvm(23017): Pending exception is:
09-29 09:10:31.058 I/dalvikvm(23017): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(-1, class android.widget.ListView) with Adapter(class xamarin.forms.platform.android.ListViewAdapter)]
09-29 09:10:31.058 I/dalvikvm(23017): (raw stack trace not found)
`