Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Xamarin.Forms setting ListView.SelectedItem = null causes app to hang/crash when using IQueryable<T> as ItemsSource in v1.6.0 #1575

Closed
Redth opened this issue Sep 23, 2017 · 21 comments

Comments

@Redth
Copy link

Redth commented Sep 23, 2017

I'm attaching a solution which reproduces an issue I'm having since updating to 1.6.0 of Realm.

Basically, on Android, when we have a Xamarin.Forms ListView and use IQueryable<TRealmObject> as the ItemsSource and inside the ListView.ItemSelected event handler, set the ListView.SelectedItem = null; it's causing the app to crash / hang without a usable managed stack trace.

This works fine in v1.5.0 of Realm. It does not seem to affect iOS.

RealmFormsSelectedItemRepro.zip

@Redth
Copy link
Author

Redth commented Sep 23, 2017

Side note, this targets android 8.0 so you'll need the Xamarin alpha channel installed to use the repro solution. I'm running this on an Android Oreo device as well, not sure that matters though.

@nirinchev
Copy link
Member

I took only a brief look at the repro code (haven't been able to run it yet) and I'd say it's very surprising since that shouldn't affect Realm at all unless the data-binding engine is doing something it's not supposed to do. Unfortunately, we're quite busy this week so won't be able to look into it until early next week. In the meantime, if you investigate further, keep us posted and we'll do our best to offer assistance.

@nirinchev
Copy link
Member

I was able to identify the problem - it's caused by Realm collections conforming to the IList interface added by #1469. When I remove the IList conformance, things work as expected and you don't get the crash. Considering, it's working fine on iOS and I can see no reason why setting a view property to null is affected by its datasource's interface conformance, I am inclined to think it's a bug with Xamarin.Forms on Android or Xamarin.Android itself.

@krdmllr
Copy link

krdmllr commented Oct 29, 2017

Hey, i run into the same problem with IRealmCollection. Did you find a solution?

@nirinchev
Copy link
Member

Unfortunately, we don't have anything yet.

@davidmclennan
Copy link

Also having the same problem and it's killing my project.

@yuv4ik
Copy link

yuv4ik commented Jan 15, 2018

Have the same issue. Do you have any plans of fixing it?

@davidmclennan
Copy link

Is there any suggestion for a hack or workaround while we wait for an official fix?

@krdmllr
Copy link

krdmllr commented Jan 22, 2018

i subscribe to the change event and add each item to an observablecollection/remove items that are deleted.

@yuv4ik
Copy link

yuv4ik commented Jan 22, 2018

@davidmclennan As a workaround you can downgrade to v1.5.0 since the bug is introduced in v1.6.0

@davidmclennan
Copy link

@yuv4ik When I uninstall the latest stable, then install 1.5 and run I get:

System.EntryPointNotFoundException: shared_realm_install_callbacks

When my code hits Realm.GetInstance(). Does this mean the old version is still somehow installed?

@yuv4ik
Copy link

yuv4ik commented Jan 25, 2018

@davidmclennan If I remember correctly I had to completely uninstall the previous version of the app with Realm 1.6.0 on the targeting device before deploying the new app with Realm 1.5.0, due to some exception. Unfortunately cannot recall it now.

@davidmclennan
Copy link

davidmclennan commented Jan 25, 2018

@yuv4ik Uninstalling the app alone still gave me the error but a clean + rebuild fixed it, for anyone else having the problem. Thank you!

@kyurkchyan
Copy link

Have spent tones of time trying to understand what's the cause.... I'm having the same issue.

@CruzatAdrian
Copy link

I hit the same problem, on 3.0.0 and 2.0.0. Any idea if there is an official fix incoming?

@nirinchev
Copy link
Member

There will be a fix, but we don't have timeline yet.

@GetTheBalanceRight
Copy link

Is there a workaround for 3.0.0? I am running into the same issue.

@CruzatAdrian
Copy link

CruzatAdrian commented May 19, 2018

@ctearpak I am using a simple workaround that uses the Subscribe for notifications method built into Realm Queries.

	public partial class SimpleList : ContentPage
	{
		
		ObservableCollection<Item> observableItems = new ObservableCollection<Item>();
		IRealmCollection<Item> rawItems;
		
		public class SimpleList()
		{
			InitializeComponent();
			
			rawItems = realm.All<Item>().AsRealmCollection();

			foreach (Item item in rawItems)
			{
				observableItems.Add(item);
			}

			rawItems.SubscribeForNotifications((sender, changes, error) =>
			{
				if (changes != null)
				{
					foreach (int i in changes.DeletedIndices)
					{
						observableItems.RemoveAt(i);
					}
			
					foreach (int i in changes.InsertedIndices)
					{
						observableItems.Insert(i, rawItems[i]);
					}
					foreach (int i in changes.ModifiedIndices)
					{
						observableItems.RemoveAt(i);
						observableItems.Insert(i, rawItems[i]);
					}

				}

				if (error != null)
				{
					// TODO: Handle Exceptions
				}


			});

			MyListView.ItemsSource = observableItems;
			
		}
		
		async void Handle_ItemSelected(object sender, ItemTappedEventArgs e)
		{

			if (e.Item == null)
			{
				return;
			}
			
			// DO Stuff

			// Deselect Item
			((ListView)sender).SelectedItem = null;
		}
		
	}

Create an observable list that will be bound to the front end ListView (ObservableItems in this example), and populate it with the elements return from the initial query. Then use the SubscribeForNotifications method to listen to any changes to the live query. In this example I am just updating the Observable List to match exactly the live query. According to the documentation, deleted indices refer to the state the list was before the notified changes (i.e. should be done first), and changed and added indices refer to the state of the list after the notified changes (i.e. should be done after removing the indices).
This should give you the desired functionality until a fix is pushed. Hope this helps!

EDIT: I forgot to add, this worked on me on 2.0, but it should work on 3.x

@GetTheBalanceRight
Copy link

GetTheBalanceRight commented May 20, 2018

@CruzatAdrian: Thank you. This did work to solve as a workaround...

However, I just switched my application over to using Prism, and don't seem to have this issue with the following (pseudo) code...

public class ListViewDemoViewModel: ViewModelBase
{
    ObservableCollection<MyObject> _allObjects;
    MyObject _selectedObject;

    public ObservableCollection<MyObject> AllObjects 
    { 
       get 
       { 
            return _allObjects; 
        } 
        private set 
        {   
            SetProperty(ref _allIObjects, value); 
         } 
     }

    public MyObject SelectedObject 
    { 
       get 
       { 
            return _selectedObject; 
        } 
        private set 
        {   
            SetProperty(ref _selectedObject, value); 
         } 
     }

     public ICommand ObjectTapped => new Command<MyObject> ( async (i) => await SelectObject(i));

     async Task SelectObject(MyObject o)
     {
          ...
          SelectedObject = null;
     }

     ...
}

The following ListView XAML ties it all together...

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
             x:Class="MyApp.Views.ListViewDemoPage"
             >
    <ContentPage.Content>
        <ListView
            ItemsSource="{Binding AllObjects}"
            VerticalOptions="Fill" BackgroundColor="Transparent" 
            CachingStrategy="RecycleElement"
            SelectedItem="{Binding SelectedObject, Mode=TwoWay}"
            >
            <ListView.Behaviors>
                <b:EventToCommandBehavior EventName="ItemTapped"
                                          Command="{Binding ObjectTapped}" 
                                          EventArgsParameterPath="Item"/>
                                          
            </ListView.Behaviors>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding SomeProperty}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

Maybe this will help someone who is thinking of switching to using Prism, or is in an early dev stage. It looks like the ViewModelBase's BindableBase takes care of this stuff. updates to the object using prism are updating in the table, so at least I know that the change events are firing.

If you use Prism, are you familiar with how this also works?

It's all new to me, so if something that I put in here doesn't make sense, or is an anti-pattern, let me know.... But it seems to have solved my problem (and Prism is pretty cool.)

EDIT:
You still need to apply the workaround for things to disappear from the list when the list is updated...
I wrote a class to do this wherever it is needed:

public static class RealmCollectionWorkaround
    {
        public static void NotificationSubscription<T>(IRealmCollection<T> realmCollection, ObservableCollection<T> observable)
        {
            realmCollection.SubscribeForNotifications((IRealmCollection<T> sender, ChangeSet changes, Exception error) =>
            {
                if (changes != null)
                {
                    foreach (int i in changes.DeletedIndices)
                        observable.RemoveAt(i);
                    foreach (int i in changes.InsertedIndices)
                        observable.Insert(i, realmCollection[i]);
                    foreach (int i in changes.ModifiedIndices)
                    {
                        observable.RemoveAt(i);
                        observable.Insert(i, realmCollection[i]);
                    }
                }

                if (error != null)
                {
                    Console.WriteLine("Exception thrown\n " + error);
                }
            });
        }
    }

@nirinchev
Copy link
Member

Here's the prerelease nugets that address this:

https://drive.google.com/file/d/1GbiaoVcZlTKW47rKyc543VLkahi1flp1/view?usp=sharing
https://drive.google.com/file/d/17QB54r_7q1xSJ3C-yi567O_UPyK7vGj1/view?usp=sharing

@manutdkid77
Copy link

Thanks for the fix.
Works for me with 3.1.0-alpha-392 from myGet 👍

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants