We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. Image for the cookie policy date
close icon

How to insert item into SfAutoComplete.DataSource without blocking UI thread?

Hello,


In my solution I have SfListView with up to 100 SfAutoComplete, each has same ObservableCollection (about 50k items) as DataSource. 


At the beginning I had some performance problem with creating this kind of list (it takes few seconds, so not acceptable for UI), but with some trick it was possible to solve it and now it works perfect and fast. First I noticed that there is very big performance cost for sfAutoComplete.DataSource = big50kObservableCollection, but it is very slow only if sfAutoCompete.IsVisible and also it needs to be invoked from the UI thread but only if "(autoComplete.IsDropDownOpen || (autoComplete.Text.Length>0 && autoComplete.IsFocused))". Anyway, to make long story short, I could find the way to populate all with data in background thread without blocking UI. 


The problem is that now I need to modify big50kObservableCollection.


1. if user select any item, I want to mark it as favorite and move (remove and insert) at the beginning of the list so next time it will be suggested from the first character. 

2. if user just enter text which is not in the big50kObservableCollection I want to make it favorite and insert it at the beginning of the list.


The problem is that even if all dropdowns are closed big50kObservableCollection.Remove and big50kObservableCollection.Insert needs to be invoked from the UI thread and because of something happened during SfAutoComplete.Handle_CollectionChanged it takes very long. When 4 sfAutoComplete are connected to big50kObservableCollection simple big50kObservableCollection.Insert(0, item) or big50kObservableCollection.Remove(item) takes over 2s, so completely not acceptable. 


I tried to disconnect big50kObservableCollection from all sfAutoComplete, modify it, and connect again in background thread in same way as I did at the beginning, it seems to be impossible, because it is impossible to change already connected sfAutoComplete.DataSource. sfAutoComplete.DataSource=null or sfAutoComplete.DataSource=new ObservableCollection<ItemType>() seems to be ignored.


Could you advise me any good way how to change sfAutoComplete.DataSource without blocking UI for few seconds?


Best regards and thank you in advance for your help.


Radek




15 Replies

MK Muneesh Kumar G Syncfusion Team August 23, 2019 01:17 PM UTC

Hi Radoslaw, 
 
Greetings from Syncfusion.  
 
We have analyzed your requirement “While change the DataSource dynamically with 50k items in SfAutoComplete blocking UI for few seconds” from our side. We are unable to reproduce the reported issue in our end. We have prepared the simple sample with given details.  
 
Sample Link: 
 
Since we are not aware of your exact application scenario, we were not able to reproduce this at our end, can you please revert us by modifying the sample based on your application along with replication procedure. This will be helpful for us to provide you better solution at the earliest.  
 
Regards, 
Muneesh Kumar G 



RK Radoslaw Kubas August 23, 2019 03:40 PM UTC

Hello,

I created sample based on your sample to demonstrate problem. 

Please take a note how long application starts now. It is because more SfAutoComplete use same ObservableCollection as DataSource and than page initialisation take very long. As I mentioned before it could be solved with some tricks, but actually I believe that also there is a place for some upgrade on your site, because simple SfAutoComplete.DataSource=BigCollection, not suppose to take so long, especially if blocking UI thread. I believe that even if some initialisation is necessary, big part of it have to be done in background thread and just don't open dropdown till initialisation complete. 

Main differences with your code are:
  1. All SfAutoComplete use same ObservableCollection as DataSource. It is important because of 2 reasons. First it is not good idea to initialise 100 x 50k business object. Second reason is that changes in one autocomplete source needs to be propagated to others.
  2. AutoComplete_SelectionChanged is moved to the MainPage, so it changes BookInfoRepository.Big50ObservableCollection
To reproduce problem just try to select any item from Autocomplete and see how long it takes to modify source collection.

If you need any more information please let me know. I hope it can be solved with some trick or just fixed in future releases.

Regards,
Radek

Attachment: AutoCompleteWithListView2_6296e0a8.zip


MK Muneesh Kumar G Syncfusion Team August 27, 2019 02:15 PM UTC

Hi Radoslaw Kubas, 
 
We have analyzed the reported issue “While change the DataSource dynamically with 50k items in SfAutoComplete blocking UI for few seconds” with given sample. We can resolve the reported issue using the asynchronous method for remove and insert the selected item from suggestion box. We have modified the sample for your reference. Please try the attached below sample and let us know if you have any concern on this. 
 
Code snippet for clear and then update the collection: 
private async void AutoComplete_SelectionChanged(object sender, Syncfusion.SfAutoComplete.XForms.SelectionChangedEventArgs e) 
        { 
            var bc = (BookInfoRepository)BindingContext; 
            string item = e.Value?.ToString(); 
            if (!string.IsNullOrEmpty(item)) 
            { 
                await ChangeDataSource(bc, item); 
            } 
        } 
        private async Task<string> ChangeDataSource(BookInfoRepository bc, string item) 
        {    
            return await Task.Run(() => 
            { 
                int index = bc.Big50ObservableCollection.IndexOf(item); 
                bc.Big50ObservableCollection.Move(index, 0); 
                return string.Empty; 
            }); 
        } 
 
 
Please check the above sample and let us know if you require any further assistance on this. 
 
Regards,  
Muneesh Kumar G  



RK Radoslaw Kubas August 27, 2019 03:25 PM UTC

Hello,
Thank you for your answer. I checked the sample both on iOS and Android. On Android it seems to works fine and as fast expected but in iOS there is an exception:

UIKitThreadAccessException: UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread. at UIKit.UIApplication.EnsureUIThread ()

And as mentioned before using Device.BeginInvokeOnMainThread(()=>{bc.Big50ObservableCollection.Move(index, 0); } solve exception problem but block UI for very very very long.

It would be amazing to have some SfAutoComplete iOS platform fix in coming weekly releases... :)

Thank you in advance.

Best regards,
Radek


RK Radoslaw Kubas August 27, 2019 07:47 PM UTC

Hello again,

I tested more on Android and unfortunately it has similar problem as iOS, just not every time. On iOS exception is mandatory, but on Android there is an exception but only in case when dropdown is open during update. In that case there is :

Android.Util.AndroidRuntimeException: 'Only the original thread that created a view hierarchy can touch its views.'

To see it just choose any item from autocomplete sugestions and than change to next autocomplete entry and open new auto complete drop down. 

To make it more visible it is enough to modify in your sample

private async Task<string> ChangeDataSource(BookInfoRepository bc, string item)
{   
      return await Task.Run(async () =>
      {
           int index = bc.Big50ObservableCollection.IndexOf(item)
    
           await Task.Delay(5000);
           
           bc.Big50ObservableCollection.Move(index, 0);
           return string.Empty;
        });
}

I hope those information will help to reproduce and fix the problem.

Regards,
Radek



MK Muneesh Kumar G Syncfusion Team August 28, 2019 12:44 PM UTC

Hi Radoslaw Kubas,  
  
We have analyzed the reported issue in SfAutoComplete. We can resolve the reported issue using the task cancellation. We have modified the sample for your reference. Please try the attached below sample and let us know if you have any concern on this.  
  
Code snippet: 
        private async void AutoComplete_SelectionChanged(object sender, Syncfusion.SfAutoComplete.XForms.SelectionChangedEventArgs e) 
        { 
            if (cancellationTokenSource != null) 
            { 
                cancellationTokenSource.Cancel(); 
            } 
            cancellationTokenSource = new CancellationTokenSource(); 
            try 
            { 
                var bc = (BookInfoRepository)BindingContext; 
                string item = e.Value?.ToString(); 
                if (!string.IsNullOrEmpty(item)) 
                { 
                    await ChangeDataSource(bc, item, cancellationTokenSource.Token); 
                } 
            } 
            // *** If cancellation is requested, an OperationCanceledException results. 
            catch (OperationCanceledException ex) 
            { 
                
            } 
            catch (Exception exc) 
            { 
                 
            } 
        } 
        CancellationTokenSource cancellationTokenSource; 
        private async Task<string> ChangeDataSource(BookInfoRepository bc, string item, CancellationToken cancellationToken) 
        { 
            return await Task.Run(async() => 
            { 
                int index = bc.Big50ObservableCollection.IndexOf(item); 
                await Task.Delay(5000); 
                bc.Big50ObservableCollection.Move(index, 0); 
                return string.Empty; 
            }, cancellationToken); 
        } 
  
Sample Link:  
 
Please check the above sample and let us know if you require any further assistance on this.  
 
Regards,  
Muneesh Kumar G 
 



RK Radoslaw Kubas August 28, 2019 01:29 PM UTC

Hello,
Thank you for the new sample and hint. I checked proposed solution just now. On Android it seems to be fine now. Thank you!
Unfortunately there is still same problem on iOS. This time exception is catched, but if you check inside, from the first insert there is same exception as before:

UIKitThreadAccessException: UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread. at UIKit.UIApplication.EnsureUIThread ()

and it seems collection is never updated (at lease there is no visible result in autocomplete suggestions)
Anyway thank you very much for help with Android and I hope it will be possible to find soon solution for iOS too.
Best regards,
Radek


MK Muneesh Kumar G Syncfusion Team August 29, 2019 12:35 PM UTC

Hi Radoslaw, 
 
We have checked the reported issue in iOS platform and with the same collection maintained for all the controls, the CollectionChanged is getting invoked every time. Hence the delay occurs and please use different collections for the controls. 
 
And we are currently checking the exception in iOS platform. And we will update the further details on or before August 30, 2019. 
 
Regards,  
Muneesh Kumar G 
 



RK Radoslaw Kubas August 29, 2019 03:27 PM UTC

Hello,

Thank you very much for your answer and checking the issue. I'm looking forward for next update. 

Anyway, since I spent hours :) analyzing the issue and trying to find possible work around, I would like to share little bit my thoughts. Maybe it would be helpful for you.

As I explained before in my solution, I have SfListView with up to 99 items, each item has 1 SfAutoComplete (with about 50k items collection). I check how long take Move(0, oldIndex) depending on involved SfAutoComplete.

08-29 15:09:58.342: I/mono-stdout(20733): [AWK] Person 1
08-29 15:10:11.395: I/mono-stdout(20733): [AWK] AddCustomFirstName: Radovan
08-29 15:10:11.397: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:10:12.118: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:00.7208671
08-29 15:10:14.667: I/mono-stdout(20733): [AWK] Person 2
08-29 15:10:20.107: I/mono-stdout(20733): [AWK] AddCustomFirstName: Martina
08-29 15:10:20.107: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:10:21.574: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:01.4668344
08-29 15:10:24.952: I/mono-stdout(20733): [AWK] Person 3
08-29 15:10:28.858: I/mono-stdout(20733): [AWK] AddCustomFirstName: Ellen
08-29 15:10:28.859: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:10:31.035: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:02.1758533
08-29 15:10:35.746: I/mono-stdout(20733): [AWK] Person 4
08-29 15:10:38.575: I/mono-stdout(20733): [AWK] AddCustomFirstName: Pierre
08-29 15:10:38.575: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:10:41.467: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:02.8922875
08-29 15:10:45.308: I/mono-stdout(20733): [AWK] Person 5
08-29 15:10:48.437: I/mono-stdout(20733): [AWK] AddCustomFirstName: Alexandra
08-29 15:10:48.438: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:10:51.903: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:03.4653927
08-29 15:10:54.425: I/mono-stdout(20733): [AWK] Person 6
08-29 15:10:56.743: I/mono-stdout(20733): [AWK] AddCustomFirstName: Lena
08-29 15:10:56.744: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:11:01.038: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:04.2940108
08-29 15:11:03.454: I/mono-stdout(20733): [AWK] Person 7
08-29 15:11:07.584: I/mono-stdout(20733): [AWK] AddCustomFirstName: Denis
08-29 15:11:07.584: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:11:12.177: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:04.5929825
08-29 15:11:19.357: I/mono-stdout(20733): [AWK] AddCustomFirstName: Pauline
08-29 15:11:19.357: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:11:24.072: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:04.7148189

99 SfAutoComplete CREATED

08-29 15:11:35.214: I/mono-stdout(20733): [AWK] AddCustomFirstName: Angela
08-29 15:11:35.214: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:11:40.560: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:05.3464499
08-29 15:11:58.473: I/mono-stdout(20733): [AWK] AddCustomFirstName: Andrea
08-29 15:11:58.473: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:12:03.487: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:05.0136593
08-29 15:12:17.412: I/mono-stdout(20733): [AWK] AddCustomFirstName: Zorica
08-29 15:12:17.413: I/mono-stdout(20733): [AWK] Starting Stopwatch AutoComplete
08-29 15:12:22.396: I/mono-stdout(20733): [AWK] Stopwatch AutoComplete stopped at 00:00:04.9833588
 
As you can see, time necessary for Move grow very fast till there are 7 SfAutoComplete involved, after that it stay same, more or less 5second. I guess it is because of very nice optimization of SfListView, and reusing already created controls (very nice, btw!).

Anyway, it seems that each time collection is modified each SfAutoComplete performs any very time-consuming operation (it takes 0.7s even when there is only 1). It is very problematic, later it works very, very fast, but initialization and update later is very slow. I have no idea if there is a chance to make it faster, but maybe it would be good idea to let populate data to this control not from ObservableCollection, but from something like "SfAutoCompleteDataSource" where items could be stored in necessary form all the time. In my case it would be game changer:
  1. I could prepare my 50k data set in correct order and in format before, and then just send initial data to the SfAutoCompleteDataSource in correct format, so initiation could be instant.
  2. Add, Remove, Insert, Move operations could be much faster, directly on SfAutoCompleteDataSource, without destroying its special items order inside, and I guess it would be not necessary to perform any very time consuming operation each time ObservableCollection is changed
  3. Since there could be in my case only 1 SfAutoCompleteDataSource even if all time-consuming operations still would be necessary, it could be done only once, not for each involved SfAtuoComplete.
Please consider this idea, it will be very, very big performance buster.

Regards,
Radek

  


MK Muneesh Kumar G Syncfusion Team August 30, 2019 12:28 PM UTC

Hi Radoslaw,  
 
Thanks for your update, currently we are validating this. We will update you the status of this on September 3rd, 2019. We appreciate your patience until then.  
 
Thanks, 
Muneesh Kumar G 



KG Kanimozhi Gunasekaran Syncfusion Team September 4, 2019 02:02 PM UTC

Hi Radoslaw,

We are checking the performance calculated for 5000000 items. Since the performance issue need to be checked further. We will update the further details on or before Sept 12,2019.

Regards,
Kanimozhi G


MK Muneesh Kumar G Syncfusion Team September 13, 2019 01:41 PM UTC

Hi Radoslaw,   
  
Due to the complexity we need some more time to analyze on this. We will update you the status of this on September 17th, 2019. We appreciate your patience until then.   
  
Thanks,  
Muneesh Kumar G  



MK Muneesh Kumar G Syncfusion Team September 17, 2019 01:09 PM UTC

Hi Radoslaw, 
  
Thanks for the patience. 
  
We have checked the Single collection with all SfListView items. It loads fast and if we change the selection, it takes time to change all the SfListView item collections.  
 
So, if we create a new collection for each SfListView item, it takes more time to load and hangs when we scroll the SfListView. 
  
In mobile app with 5000000 items loading is not a correct approach and it's not supported by SfComboBox in iOS. So please validate your requirement and change it to support with comfortable data. 
 
Thanks, 
Muneesh Kumar G. 
 



RK Radoslaw Kubas September 17, 2019 02:12 PM UTC

Hello,

 

Thank you for your answer and time you spend analyzing the problem. Unfortunately, I can’t agree with result. I believe it was some kind of misunderstanding so please analyze this problem little more. Maybe there will be solution.

 

1stPlease take a note that my collection is not 5000000 (5000k), but 50000 (50k), so 100 times smaller.

2ndThe problem is about SfAutoComplete, not SfComboBox.

 

I agree that putting 5000k itmes into SfCombobox may be not the best design decision but putting 50k items into SfAutoComplete seems to be supported. There is even an example in Android Syncfusion Compontnts sample app, showing very nice performance for 100k items. 

 

Also please take a note that all problems are because some time-consuming operation have to be done in the UI thread even if there is no obvious reason for that (all dropdowns are closed). To make things worse if more than one SfAutoComplete use same source, collection modification takes 2,3,4 times more than for 1 SfAutoComplete. It seems this very time-consuming operation in UI thread is repeated many times, even if it was enough to do it once and use same result for all SfAutoComplete.

 

I hope you can continue investigation and find better solution (both for iOS and Android). Even if there seems to be some work around in Android (in your example) it works not so stable, throw exceptions and after more repeats usually crashes app anyway.

 

Best regards,

Radek



RB Rabhia Beham Kathar Mideenar Syncfusion Team September 18, 2019 01:58 PM UTC

Hi Radoslaw,
 
Thanks for the update.
 
In above sample you have used the 50K items in 100 listView items. So we mentioned the 5M items in previous update. And in SfAutoComplete we used individually it's loaded correctly and working fast. If you used the 50K items initially the control was taken more load time. So we have loaded the datasource on editing in first time. It will reduce the loading time and the SfAutoComplete work as expected. Please get the sample from below,
 
Link: https://www.syncfusion.com/downloads/support/directtrac/general/ze/AutoCompleteWithListView-2311736605

Please let us know if you have any concern.

Regards,
Rabhia Beham K.


Loader.
Live Chat Icon For mobile
Up arrow icon