6

I have recently tested the CachingStrategy for a ListView in a Xamarin Forms App with 1000 items in the list. List items are being created from a data template of ViewCell. I Tried using RecycleElement option for CachingStrategy.

When I did profiling, using Xamarin Profiler, for Android App deployed on Xamarin Anroid Player (emulator) I noticed that when I scroll through the list the memory allocation doesn't increase (on allocations summary tab). But, when I did profiling for iPhone App on emulator, I noticed that no data is being displayed on Allocations Summary tab. So I captured some snapshots while scrolling through the list and noticed whenever I scroll through the list (up or down), the memory allocation keeps increasing.

Why RecycleElement is not working for iOS (iPhone)?

I am using Mac for development. Here are my tools:

=== Xamarin Studio ===

Version 5.10.1 (build 3) Installation UUID: 7ae992a3-b710-4297-ba1d-0c519fbb2ea8 Runtime: Mono 4.2.1 (explicit/6dd2d0d) GTK+ 2.24.23 (Raleigh theme)

Package version: 402010102

=== Xamarin.Profiler ===

Version: 0.24.0.0 Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

=== Apple Developer Tools ===

Xcode 7.1.1 (9081) Build 7B1005

=== Xamarin.iOS ===

Version: 9.2.1.54 (Enterprise Edition) Hash: eb4c1ef Branch: master Build date: 2015-12-01 02:12:30-0500

=== Xamarin.Android ===

Version: 6.0.0.34 (Enterprise Edition) Android SDK: /Users/haider/Library/Developer/Xamarin/android-sdk-macosx Supported Android versions: 4.0.3 (API level 15) 4.4 (API level 19) 5.0 (API level 21) 5.1 (API level 22) 6.0 (API level 23)

SDK Tools Version: 24.4.1 SDK Platform Tools Version: 23.1 rc1 SDK Build Tools Version: 23.0.2

Java SDK: /usr java version "1.7.0_71" Java(TM) SE Runtime Environment (build 1.7.0_71-b14) Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)

=== Xamarin Android Player ===

Version: 0.6.5 Location: /Applications/Xamarin Android Player.app

=== Xamarin.Mac ===

Version: 2.4.0.109 (Starter Edition)

=== Build Information ===

Release ID: 510010003 Git revision: f2021a209d66d49cbc0649a6d968b29040e57807 Build date: 2015-12-01 10:43:40-05 Xamarin addins: dfd4f5103e8951edbc8ac24480b53b53c55e04ff Build lane: monodevelop-lion-cycle6-baseline

=== Operating System ===

Mac OS X 10.11.1 Darwin Haiders-MacBook-Pro.local 15.0.0 Darwin Kernel Version 15.0.0 Sat Sep 19 15:53:46 PDT 2015 root:xnu-3247.10.11~1/RELEASE_X86_64 x86_64

Haider
  • 1,488
  • 2
  • 15
  • 29
  • This is a very good question. I am not sure but maybe it is a bug. Did you post it on Bugzilla? – Ege Aydın Dec 22 '15 at 19:08
  • No. I guess they mentioned StackOverflow for asking questions. – Haider Dec 26 '15 at 07:52
  • Hi Haider! Please let me know if you have any follow up questions to the solution below! If you don't have any follow up questions, let's mark this question as Answered to help fellow developers in the future when they have a similar question! – Brandon Minnick Dec 01 '16 at 06:37

1 Answers1

1

Here's A Few Things To Check

  1. In the Xamarin Profiler, ensure that you are only looking for the custom ViewCell class, and take multiple snapshots to trigger the garbage collector. It may be something else that is causing the memory leak if the number of ViewCells is not increasing. If the number of ViewCells is increasing, move on to suggestions 2 & 3, below. Xamarin Profiler ViewCell example

  2. In the ViewCell code, make sure to override OnBindingContextChanged() and set the controls' properties in OnBindingContextChanged(), rather than in the ViewCell's constructor. I added some sample code below that shows how to implement the ListViewCachingStrategy.RecycleElement strategy using a custom ViewCell.

  3. If you are subscribing an Event Handler for the ViewCell (to add a Context Action for example), make sure to subscribe the Event Handler in the OnAppearing() method of the ViewCell class and unsubscribe the Event Handler in the OnDisappearing() method of the ViewCell class. I've included a comment in the example ViewCell code below.

ListView Using RecycleElement

ListView = new ListView(ListViewCachingStrategy.RecycleElement)
{
    DataTemplate(typeof(CustomViewCell))
};

ViewCell

public class CustomViewCell : ViewCell
{
    Label _myLabel;
    MenuItem _deleteAction;

    public CustomViewCell()
    {
        _myLabel = new Label();

        View = _myLabel;
    }

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();

        _myLabel.Text = "";

        var item = BindingContext as MyModel;
        if (item != null)
        {
            _myLabel.Text = item.Text;
        }
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();

        //Subscribe ViewCell Event Handlers
        _deleteAction.Clicked += HandleDeleteClicked;
        ContextActions.Add(_deleteAction);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        //Unsubscribe ViewCell Event Handlers
        _deleteAction.Clicked -= HandleDeleteClicked;
        ContextActions.Remove(_deleteAction);
    }

    void HandleDeleteClicked(object sender, EventArgs e)
    {
        //Code to handle when the delete action is tapped
    }
}

ViewCell Model

public class MyModel
{
    [PrimaryKey]
    public int ID { get; set; }

    public string Text { get; set; }
}
Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
  • Why don't you want to use bindings? – François Feb 03 '17 at 12:46
  • Hi @François! When using ListViewCachingStrategy.RecycleElement, you are required to set assign the bindings by overriding the ViewCell's OnAppearing appearing method. This ensures that the data is recycled when the ViewCell scrolls off of the screen. Here is a link to the Xamarin documentation: https://developer.xamarin.com/guides/xamarin-forms/user-interface/listview/performance/#RecycleElement – Brandon Minnick Feb 03 '17 at 13:17
  • Tks but it does say "This can be accomplished by using data binding to display cell data". And then present your method as an alternative. – François Feb 03 '17 at 13:19
  • This example is still using data binding, just not the traditional way of using the SetBinding method. The ViewCell is data-bound to the Model in OnAppearing by referencing the BindingContext. In the sample above, we need to assign an EventHandler to each ViewCell, and we need to ensure that the EventHandler is unsubscribed when the ViewCell scrolls off of the screen, because, if we don't unsubscribe the EventHandler, we will experience a memory leak. The only way to assign and unassign the EventHandler properly is to override the OnAppearing and OnDisappearing methods. – Brandon Minnick Feb 03 '17 at 13:36
  • That's the answer I was looking for as I Indeed encounter issue. But they should fix bindings rather than us have good too deal with it. – François Feb 03 '17 at 16:14
  • This works fine on iOS, on Android `OnBindingContextChanged`is not called. Removing `ListViewCachingStrategy.RecycleElement` works on Android but then scrolling the list view is very slow. Any Idea what could be the problem? – rraallvv Apr 09 '18 at 06:38