Windows Phone与图像的高内存使用率

时间:2015-11-05 14:31:26

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

我正在使用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 i的性能和诊断工具能够确定应用程序启动时的内存使用量约为16 Mb。当我将测试图像加载到应用程序时,它最多可拍摄58 MB。好像它仍然使用了图像的全尺寸。和(仅用于测试)当我把decodepixelheight和宽度拉开时,它飙升到大约350 MB。我完全不知道为什么它会为图像使用这么多内存。

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

1 个答案:

答案 0 :(得分:2)

您的图片在压缩时使用34 MB存储 。要显示,需要解压缩。位图图片每像素使用4个字节(每个颜色通道一个字节,RGB,加上alpha通道一个字节)。 因此,单个500万像素的图片将使用大约20 MB的RAM。这就是为什么尽可能频繁地使用DecodePixelHeight / DecodePixelHeight这是至关重要的。

但是,有时,你操纵巨大的图片(例如Lumia 1020的38 MP图片,每个150 MB的RAM!)。为此,诺基亚发布了成像SDK(现在由微软维护),允许您处理部分图片,而不必在内存中加载完整的东西。

在您的情况下,主要问题是您一次加载所有缩略图,即使其中只有少数同时显示。如果要减少使用的内存量,则必须懒惰地加载缩略图(即,仅在需要时)。一种方法是存储文件位置而不是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;
            }
        }
    }
}