我正在使用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。我完全不知道为什么它会为图像使用这么多内存。
因为应用程序必须能够使用更多更大的图像,所以我需要找到一种方法来减少内存使用量。有谁知道如何做到这一点?
答案 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;
}
}
}
}