You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI CollectionView内存占用过高问题求助

内存占用过高的问题分析与修复方案

核心问题1:CollectionView被ScrollView包裹

你的布局中CollectionView嵌套在ScrollView内部,这会直接禁用CollectionView的UI虚拟化功能。CollectionView原本只会渲染当前可见区域的项,剩余项在滚动时按需加载以节省内存,但外层ScrollView会强制CollectionView渲染所有1000+项的完整控件树(每个项包含Button、Entry等控件),这是内存占用暴涨至1.4GB的主要原因。

修复方法:移除外层ScrollView,让CollectionView直接作为布局的一部分:

<!-- 修改后的MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Ö3_Playlist"
             x:Class="Ö3_Playlist.MainPage">

    <VerticalStackLayout
        Spacing="25"
        Padding="30,0"
        VerticalOptions="Start">

        <Button
            x:Name="CounterBtn"
            Text="Parse"
            SemanticProperties.Hint="Counts the number of times you click"
            Clicked="OnCounterClicked"
            HorizontalOptions="Center" />

        <ActivityIndicator IsVisible="{Binding IsBusy}" Color="White"/>

        <!-- 直接放置CollectionView,移除外层ScrollView -->
        <CollectionView ItemsSource="{Binding Entries}" VerticalOptions="FillAndExpand">
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="local:SongEntry">
                    <HorizontalStackLayout HorizontalOptions="FillAndExpand">
                        <Button Text="Open" Command="{Binding OpenCommand}"/>
                        <Entry Text="{Binding Name}" IsReadOnly="True"/>
                        <Button Text="{Binding IsLiked}" Command="{Binding ClickedCommand}"/>
                    </HorizontalStackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
        
    </VerticalStackLayout>
</ContentPage>

核心问题2:使用List而非ObservableCollection

当前Entries使用List<SongEntry>,虽然通过[ObservableProperty]标记,但List<T>不实现INotifyCollectionChanged接口。这意味着向列表添加单个项时,CollectionView无法高效增量更新UI,可能导致不必要的整体刷新,间接增加内存开销。

修复方法:将Entries改为ObservableCollection<SongEntry>:

public partial class ViewModel : ObservableObject
{
    [ObservableProperty]
    // 替换List为ObservableCollection
    ObservableCollection<SongEntry> entries = new ObservableCollection<SongEntry>();

    // ... 其他代码不变
}

其他优化点

1. 移除ViewModel的析构函数

析构函数(~ViewModel())的执行时机由CLR垃圾回收器决定,无法保证数据及时保存,还可能导致ViewModel对象被延迟回收,增加内存占用。建议在页面卸载时触发保存操作:

public partial class MainPage : ContentPage
{
    public ViewModel vm { get; set; } = new ViewModel();

    public MainPage()
    {
        BindingContext = vm;
        InitializeComponent();
        // 页面卸载时保存数据
        this.Unloaded += (s, e) => Database.Save(vm.Entries.ToList());
    }

    // ... 其他代码不变
}

2. 优化重复项检查逻辑

当前Parse方法中每次添加新项都调用Entries.FirstOrDefault遍历整个列表,当列表有1000+项时,这是O(n²)的耗时操作。可以用HashSet<string>缓存已存在的歌曲名称,将检查复杂度降为O(1):

public async Task Parse()
{
    IsBusy = true;
    var loader = new WebLoader();
    // 缓存已存在的歌曲名称(小写去重)
    var existingNames = new HashSet<string>(Entries.Select(x => x.Name.Trim().ToLower()));

    for (int i = 0; i < 10; i++)
    {
        var doc = await loader.LoadWebpage(@"https://onlineradiobox.com/at/hitradiooe3/playlist/" + i);
        var list = GetSongs(doc);
        foreach (var song in list)
        {
            var key = song.Name.Trim().ToLower();
            if (!existingNames.Contains(key))
            {
                Database.Insert(song);
                Entries.Add(song);
                existingNames.Add(key);
            }
        }
    }
    IsBusy = false;
}

3. 避免async void命令

SongEntry中的Open命令使用async void,这可能导致未捕获的异常和潜在的内存泄漏。建议改为async Task:

[RelayCommand]
private async Task Open()
{
    string uri = @"https://www.youtube.com/results?search_query=" + Name;
    await BrowserOpen_Clicked(uri);
}

内容的提问来源于stack exchange,提问作者Thorsten Schmitz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:52:36