Xamarin Forms ListView 缓存策略

6

我最近在一款Xamarin Forms应用程序中测试了包含1000个列表项的ListViewCachingStrategy。列表项是从ViewCell的数据模板创建的。我尝试使用CachingStrategy选项的RecycleElement

当我使用Xamarin Profiler对Android应用程序进行分析时,部署在Xamarin Anroid Player(模拟器)上,我注意到当我滚动列表时内存分配不会增加(在分配汇总选项卡上)。但是,当我在模拟器上为iPhone应用程序进行分析时,我注意到在分配汇总选项卡上没有显示任何数据。因此,我在滚动列表时捕获了一些快照,并注意到每当我滚动列表时(向上或向下),内存分配就会不断增加。

为什么RecycleElement在iOS(iPhone)上不起作用?

我正在使用Mac进行开发。 这里是我的工具:

=== Xamarin Studio ===

版本5.10.1(构建3) 安装UUID:7ae992a3-b710-4297-ba1d-0c519fbb2ea8 运行时: Mono 4.2.1(显式/6dd2d0d) GTK+ 2.24.23(Raleigh主题)

Package version: 402010102

=== Xamarin.Profiler ===

版本: 0.24.0.0 位置: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

=== Apple开发工具 ===

Xcode 7.1.1 (9081) 构建 7B1005

=== Xamarin.iOS ===

版本: 9.2.1.54 (企业版) 哈希值: eb4c1ef 分支: master 构建日期: 2015-12-01 02:12:30-0500

=== Xamarin.Android ===

版本: 6.0.0.34 (企业版) Android SDK: /Users/haider/Library/Developer/Xamarin/android-sdk-macosx 支持的 Android 版本: 4.0.3 (API 级别 15) 4.4 (API 级别 19) 5.0 (API 级别 21) 5.1 (API 级别 22) 6.0 (API 级别 23)

SDK 工具版本: 24.4.1 SDK 平台工具版本: 23.1 rc1 SDK 构建工具版本: 23.0.2

Java SDK: /usr java 版本 "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 ===

版本: 0.6.5 位置: /Applications/Xamarin Android Player.app

=== Xamarin.Mac ===

版本: 2.4.0.109 (入门版)

=== 构建信息 ===

发布 ID: 510010003 Git 修订版本: f2021a209d66d49cbc0649a6d968b29040e57807 构建日期: 2015-12-01 10:43:40-05 Xamarin 插件: dfd4f5103e8951edbc8ac24480b53b53c55e04ff 构建 lane: monodevelop-lion-cycle6-baseline

=== 操作系统 ===

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


这是一个非常好的问题。我不确定,但也许这是一个漏洞。你在 Bugzilla 上发布了吗? - Ege Aydın
不,我想他们提到了 StackOverflow 用于提问。 - Haider
嗨,海德尔!如果您对以下解决方案有任何后续问题,请告诉我!如果您没有任何后续问题,让我们将此问题标记为已回答,以帮助未来遇到类似问题的开发人员! - Brandon Minnick
1个回答

1
这里有几个需要检查的事项。
  1. 在 Xamarin Profiler 中,确保只查找自定义的 ViewCell 类,并且拍摄多个快照以触发垃圾回收。如果 ViewCells 的数量没有增加,可能是其他原因导致了内存泄漏。如果 ViewCells 的数量正在增加,请继续下面的建议2和3。Xamarin Profiler ViewCell example

  2. 在 ViewCell 代码中,请确保重写 OnBindingContextChanged() 并在其中设置控件的属性,而不是在 ViewCell 的构造函数中设置。我添加了一些示例代码,展示如何使用自定义 ViewCell 实现 ListViewCachingStrategy.RecycleElement 策略。

  3. 如果您正在为 ViewCell 订阅事件处理程序(例如添加 上下文操作),请确保在 ViewCell 类的 OnAppearing() 方法中订阅事件处理程序,并在 OnDisappearing() 方法中取消订阅事件处理程序。我在下面的示例 ViewCell 代码中包含了注释。

使用RecycleElement的ListView

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
    }
}

视图单元模型

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

    public string Text { get; set; }
}

为什么你不想使用绑定? - François
嗨@François!当使用ListViewCachingStrategy.RecycleElement时,您需要通过覆盖ViewCell的OnAppearing方法来分配绑定。这可以确保在ViewCell滚动出屏幕时回收数据。这是Xamarin文档的链接:https://developer.xamarin.com/guides/xamarin-forms/user-interface/listview/performance/#RecycleElement - Brandon Minnick
谢谢,但它确实说“可以通过使用数据绑定来显示单元格数据”。 然后提出你的方法作为一种替代方案。 - François
这个例子仍然使用数据绑定,只是没有使用传统的SetBinding方法。ViewCell通过引用BindingContext在OnAppearing中与Model进行数据绑定。在上面的示例中,我们需要为每个ViewCell分配一个EventHandler,并确保当ViewCell滚动出屏幕时取消订阅EventHandler,因为如果我们不取消订阅EventHandler,就会出现内存泄漏。正确分配和取消分配EventHandler的唯一方法是重写OnAppearing和OnDisappearing方法。 - Brandon Minnick
这就是我一直在寻找的答案,因为我确实遇到了这个问题。但他们应该修复绑定问题,而不是让我们去处理它。 - François
这在iOS上运行良好,在Android上OnBindingContextChanged不会被调用。 移除ListViewCachingStrategy.RecycleElement可以解决Android上的问题,但是滚动列表视图变得非常缓慢。 有什么想法是什么问题? - rraallvv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接