Only the original thread that created a view hierarchy can touch its views c#

Platform (please complete the following information):

  • OS: [Android]
  • Device: [AndroidEmulator]
  • Xamarin.Forms: [5.0.0.2125]

Describe the bug
Using the collectionview with ReactiveUI DynamicData binding causes threading issues.
Android.Util.AndroidRuntimeException: Only the original thread that created a view hierarchy can touch its views.
It didn't seem to matter which thread I observed, or subscribed on.
The Xamarin.Forms CollectionView doesn't seem to have this issue.

To Reproduce

// if not observed on the main thread scheduler, items do not appear // in the list when using the real bluetooth service but will if using // the simulated service. OBSERVE ON MAIN THREAD SCHEDULER! recentDevices .ObserveOn(RxApp.MainThreadScheduler) .SubscribeOn(RxApp.MainThreadScheduler) .Bind(out _devices) .Subscribe() .DisposeWith(Disposables);

Exceptions (if applicable)
Copy paste the exception and the stack trace

CollectionViewRenderer.UpdateItemsSource () Android.Util.AndroidRuntimeException: Only the original thread that created a view hierarchy can touch its views. JniEnvironment+InstanceMethods.CallNonvirtualVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) JniPeerMembers+JniInstanceMethods.InvokeVirtualVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) RecyclerView.SetAdapter (AndroidX.RecyclerView.Widget.RecyclerView+Adapter adapter) CollectionViewRenderer.UpdateItemsSource () CollectionViewRenderer.OnCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) (wrapper delegate-invoke) <Module>.invoke_void_object_NotifyCollectionChangedEventArgs(object,System.Collections.Specialized.NotifyCollectionChangedEventArgs) ReadOnlyObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs args) ReadOnlyObservableCollection`1[T].HandleCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) ObservableCollectionExtended`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) ObservableCollectionExtended`1[T].<SuspendNotifications>b__10_0 () AnonymousDisposable.Dispose () SortedObservableCollectionAdaptor`2[TObject,TKey].Adapt (DynamicData.ISortedChangeSet`2[TObject,TKey] changes, DynamicData.Binding.IObservableCollection`1[T] collection) ObservableCacheEx+<>c__DisplayClass27_0`2[TObject,TKey].<Bind>b__1 (DynamicData.ISortedChangeSet`2[TObject,TKey] changes) Select`2+Selector+_[TSource,TResult].OnNext (TSource value) ExceptionServicesImpl.Rethrow (System.Exception exception) ExceptionHelpers.Throw (System.Exception exception) <.cctor>b__2_1 (System.Exception ex) AnonymousObserver`1[T].OnErrorCore (System.Exception error) ObserverBase`1[T].OnError (System.Exception error) AutoDetachObserver`1[T].OnErrorCore (System.Exception exception) ObserverBase`1[T].OnError (System.Exception error) Sink`1[TTarget].ForwardOnError (System.Exception error) Select`2+Selector+_[TSource,TResult].OnNext (TSource value) Sink`1[TTarget].ForwardOnNext (TTarget value) Synchronize`1+_[TSource].OnNext (TSource value) AutoDetachObserver`1[T].OnNextCore (T value) ObserverBase`1[T].OnNext (T value) Sink`1[TTarget].ForwardOnNext (TTarget value) ObserveOnObserverLongRunning`1[TSource].Drain () <.cctor>b__17_0 (System.Reactive.ObserveOnObserverLongRunning`1[TSource] self, System.Reactive.Disposables.ICancelable cancelable) <.ctor>b__3_0 (System.Object thisObject) ConcurrencyAbstractionLayerImpl+<>c.<StartThread>b__8_0 (System.Object itemObject) ThreadHelper.ThreadStart_Context (System.Object state) ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state) ThreadHelper.ThreadStart (System.Object obj) android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. android.view.ViewRootImpl.checkThread ViewRootImpl.java:8798 android.view.ViewRootImpl.requestLayout ViewRootImpl.java:1606 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.widget.RelativeLayout.requestLayout RelativeLayout.java:380 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 android.view.View.requestLayout View.java:25390 androidx.recyclerview.widget.RecyclerView.requestLayout RecyclerView.java:4586 androidx.recyclerview.widget.RecyclerView.setAdapter RecyclerView.java:1196

How do you fix only the original thread that created a view hierarchy can touch its views?

To fix this error, wrap the code that has to be executed on UI thread in a Runnable instance passed to runOnUiThread() method.

How does runOnUiThread work?

Basically what runOnUiThread() will do is - Runs the specified action on the UI thread. It will check the current thread and if it finds its the MainThread it will execute that task immediately , otherwise first it will switch you to app MainThread and then it will execute the given task.

How do you run on UI thread in Kotlin?

runOnUiThread runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread. ... Android runOnUiThread Example..