【问题标题】:Windows Phone high memory usage with images带有图像的 Windows Phone 高内存使用率
【发布时间】:2015-11-05 14:31:26
【问题描述】:

我正在使用 MvvmCross 在 Xamarin 中开发 Windows Phone 应用程序。在这个应用程序中,用户从他的手机中选择一些图像。它们显示在列表中,然后用户使用它们进行操作。

我使用 FileOpenPicker 进行文件选择,并从这些文件中创建 BitmapImages 来显示

foreach (StorageFile file in args.Files) {
                    BitmapImage thumbnail = new BitmapImage();
                    thumbnail.DecodePixelType = DecodePixelType.Physical;
                    try {
                        using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read)) {
                            thumbnail.DecodePixelHeight = 70;
                            thumbnail.DecodePixelWidth = 70;

                            thumbnail.SetSource(fileStream);
                            fileStream.Dispose();
                        }
                    }
                    catch (OutOfMemoryException e) {
                        Mvx.Trace("MEMORY IS FULL");
                    }

在一些其他代码之后,我将这些 BitmapImages 放入 ObservableCollection 并像这样显示它们

<Image Style="{StaticResource imageListImage}" Source="{Binding Thumbnail}"/>

这没什么特别的。 我使用的测试图像的总大小为 34 MB。 使用 VS 的性能和诊断工具,我能够确定应用程序在启动时的内存使用量约为 16 Mb。当我将测试图像加载到应用程序时,它达到了 58 MB。好像它仍然使用图像的完整尺寸。并且(仅用于测试)当我将 decodepixelheight 和 width 移开时,它飙升至大约 350 MB。我完全不知道为什么它要为图像使用这么多内存。

因为应用程序必须能够使用更多更大的图像,所以我需要找到一种方法来减少内存使用量。有谁知道我该怎么做?

【问题讨论】:

    标签: c# image xaml memory windows-phone-8


    【解决方案1】:

    您的图片在压缩时使用 34 MB 的存储空间。要显示,它们需要解压缩。位图图片每个像素使用 4 个字节(每个颜色通道 RGB 一个字节,以及 Alpha 通道一个字节)。 因此,一张 5 兆像素的图片将使用大约 20 MB 的 RAM。 这就是为什么尽可能频繁地使用 DecodePixelHeight/DecodePixelHeight 至关重要。

    不过,有时您必须处理大图片(例如 Lumia 1020 的 38 MP 图片,每张 150 MB 内存!!)。为此,Nokia 发布了 Imaging SDK(现在由 Microsoft 维护),允许您处理部分图片,而不必将全部内容加载到内存中。

    在您的情况下,主要问题是您一次加载所有缩略图,即使只有少数几个会同时显示。如果要减少使用的内存量,则必须延迟加载缩略图(即仅在需要时)。一种方法是存储文件位置而不是BitmapImage,并根据需要加载图片。不幸的是,您不能直接绑定到Image 控件的路径(除非文件在应用程序的本地存储中)。 In that question,我建议为遇到类似问题的人使用自定义用户控件。

    public sealed partial class LocalImage : UserControl
    {
        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof (string),
            typeof (LocalImage), new PropertyMetadata(null, SourceChanged));
    
        public LocalImage()
        {
            this.InitializeComponent();
        }
    
        public string Source
        {
            get { return this.GetValue(SourceProperty) as string; }
            set { this.SetValue(SourceProperty, value); }
        }
    
        private async static void SourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var control = (LocalImage)obj;
    
            var path = e.NewValue as string;
    
            if (string.IsNullOrEmpty(path))
            {
                control.Image.Source = null;
            }
            else
            {
                var file = await StorageFile.GetFileFromPathAsync(path);
    
                using (var fileStream = await file.OpenAsync(FileAccessMode.Read))
                {
                    BitmapImage bitmapImage = new BitmapImage();
                    await bitmapImage.SetSourceAsync(fileStream);
                    control.Image.Source = bitmapImage;
                }
            }
        }
    }
    

    【讨论】:

    • 感谢您的回答。我现在已经找到了解决方案。虽然这不是你给我看的,但你在另一篇文章中给我的链接有解决方案。使用 file.getScaledImageAsThumbnailAsync 它使用的内存要少得多(不知道为什么)。虽然将来我的应用程序还需要显示整个图片库,所以我会看看你在那里展示的 ISupportIncrementalLoading。再次感谢您的帮助
    猜你喜欢
    • 2016-05-18
    • 1970-01-01
    • 1970-01-01
    • 2012-06-12
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多