[c#] Warum erhalte ich eine OutOfMemoryException, wenn ich Bilder in meiner ListBox habe?


Answers

Sie hatten gerade Windows Phone mit zeigen alle Bilder im Ordner "Bilder" eines Benutzers Medienbibliothek auf dem Bildschirm. Das ist unglaublich speicherintensiv und angesichts der 150 MB-Grenze für WP8-Apps ist es kein Wunder, dass Sie OOM-Ausnahmen bekommen.

Ein paar Dinge, die Sie hinzufügen sollten:

1) Setzen Sie die Eigenschaften Source und SourceUri auf null, wenn Sie das Listbox-Element nicht anzeigen. Siehe "Caching Images" in Stefans Artikel hier @ http://blogs.msdn.com/b/swick/archive/2011/04/07/image-tips-for-windows-phone-7.aspx

  BitmapImage bitmapImage = image.Source as BitmapImage;
  bitmapImage.UriSource = null;
  image.Source = null;

2) Wenn Sie auf WP8 sind, stellen Sie sicher, dass Sie DecodePixelWidth und / oder DecodePixelHeight einstellen. Auf diese Weise wird ein Bild in den Speicher geladen, die Größe wird permanent geändert und nur die geänderte Kopie wird gespeichert. Die Bilder, die in den Speicher geladen werden, können viel größer als die Bildschirmgröße des Telefons selbst sein. Es ist daher wichtig, diese auf die richtige Größe zu schneiden und nur die Größe der Bilder zu speichern. Setzen Sie BitmapImage.DecodePixelWidth = 480 (maximal), um damit zu helfen.

var bmp = new BitmapImage();

// no matter the actual size, 
// this bitmap is decoded to 480 pixels width (aspect ratio preserved)
// and only takes up the memory needed for this size
bmp.DecodePixelWidth = 480;

bmp.UriSource = new Uri(@"Assets\Demo.png", UriKind.Relative);
ImageControl.Source = bmp;

(Codebeispiel von here )

3) Warum verwenden Sie Picture.GetImage () anstelle von Picture.GetThumbnail ()? Brauchen Sie wirklich, dass das Bild den ganzen Bildschirm einnimmt?

4) Ziehen Sie in Erwägung, von ListBox zu LongListSelector zu wechseln, wenn es sich um eine exklusive WP8-App handelt. LLS hat viel, viel bessere Virtualisierung als ListBox. Wenn Sie Ihr Code-Beispiel betrachten, reicht es möglicherweise aus, dass Sie nur Ihr XAML-ListBox-Element in LongListSelector-Element ändern.

Question

Ich möchte alle Bilder, die im Windows Phone 8- ListBox gespeichert sind, in meiner benutzerdefinierten Galerie anzeigen, die eine ListBox zum Anzeigen der Bilder verwendet.

Der ListBox Code lautet wie folgt:

    <phone:PhoneApplicationPage.Resources>
        <MyApp:PreviewPictureConverter x:Key="PreviewPictureConverter" />
    </phone:PhoneApplicationPage.Resources>

    <ListBox Name="previewImageListbox" VirtualizingStackPanel.VirtualizationMode="Recycling">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel CleanUpVirtualizedItemEvent="VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1">
                </VirtualizingStackPanel>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Image Source="{Binding Converter={StaticResource PreviewPictureConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
     </ListBox>

Mit folgendem Konverter:

public class PreviewPictureConverter : System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        PreviewImageItem c = value as PreviewImageItem;
        if (c == null)
            return null;
        return c.ImageData;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Bilder werden in einer benutzerdefinierten Klasse gespeichert:

class PreviewImageItem
{
    public Picture _picture = null;
    public BitmapImage _bitmap = null;

    public PreviewImageItem(Picture pic)
    {
        _picture = pic;
    }

    public BitmapImage ImageData 
    {
        get
        {
            System.Diagnostics.Debug.WriteLine("Get picture " + _picture.ToString());
            _bitmap = new BitmapImage();
            Stream data = _picture.GetImage();
            try
            {
                _bitmap.SetSource(data); // Out-of memory exception (see text)
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Exception : " + ex.ToString());
            }
            finally
            {
                data.Close();
                data.Dispose();
                data = null;
            }

            return _bitmap;
        }
    }
}

Der folgende Code wird zum ListBox der ListBox Datenquelle verwendet:

private List<PreviewImageItem> _galleryImages = new List<PreviewImageItem>();

using (MediaLibrary library = new MediaLibrary())
{
    PictureCollection galleryPics = library.Pictures;
    foreach (Picture pic in galleryPics)
    {
        _galleryImages.Add(new PreviewImageItem(pic));
    }

    previewImageListbox.ItemsSource = _galleryImages;
};

Schließlich ist hier der "Aufräum" -Code:

private void VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1(object sender, CleanUpVirtualizedItemEventArgs e)
{
    PreviewImageItem item = e.Value as PreviewImageItem;

    if (item != null)
    {
        System.Diagnostics.Debug.WriteLine("Cleanup");
        item._bitmap = null;
    }
}

All dies funktioniert gut, aber der Code stürzt nach einigen Bildern mit einer OutOfMemoryException ab (besonders beim schnellen Scrollen). Die Methode VirtualizingStackPanel_CleanUpVirtualizedItemEvent_1 wird beim ListBox der ListBox (zB alle 2 oder 3 Listbox-Einträge) ListBox .

Was ist falsch an diesem Beispielcode?

Warum wird der Speicher nicht freigegeben (schnell genug)?




Links