очистка кэша изображений (освобождение памяти), когда изображения связаны с данными в xaml - PullRequest
2 голосов
/ 28 февраля 2012

Согласно блогу Стефана Вика, освободить память из изображений так же просто, как сделать:

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

Однако, как я могу добиться того же эффекта, если я использую привязку данных в Xaml, как это?:

// inside MainPage.xaml
<Button Tap="GetImages">Get Images</Button>
  <ListBox ItemSource="{Binding Links}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <!-- I am not binding to Image's Source directly, because I want to use the bitmap's 'Background creation' option and it's download progress event in the future -->
                <Image>
                   <Image.Source>
                   <BitmapImage UriSource="{Binding}" CreateOptions="BackgroundCreation"/>
                   </Image.Source>
                </Image>
            </DataTemplate>
        </ListBox.ItemTemplate>
 </ListBox>


//inside MainPage.xaml.cs
   public void GetImages(object sender, RoutedEventArgs e) {
        (DataContext as ViewModel).GetMeSomeImages();
   }

// inside ViewModel.cs
   public void GetMeSomeImages() {
       List<string> links = ThisMethodGetsLinks();
       Links.Clear();
       foreach(var link in links)
            Links.Add(link);
   }

   ObservableCollection<string> _links;
   public ObservableCollection<string> Links {
     get {
           if (_links == null)
               _links = new ObservableCollection<string>();
           return _links;
         }
    }

В этом случае каждое нажатие кнопки будет занимать дополнительную память, пока не произойдет сбой телефона / эмулятора. Изображения не освобождаются из памяти, несмотря на очищаемое свойство ItemSource в Listbox.

1 Ответ

2 голосов
/ 14 марта 2012

Перечисление BitmapCreateOptions определяет перечисление BackgroundCreation таким образом:

Вызывает инициализацию BitmapSource, как только он объявлен. Эта опция использует кэш изображений для ранее использованных URI. Если изображение отсутствует в кэше изображений, оно будет загружено и декодировано в отдельном фоновом потоке.

Это заставляет меня думать, что при изменении свойства UriSource и удалении старого изображения фоновый поток, обрабатывающий загрузку растрового изображения, не уведомляется, и этот фоновый поток продолжает загружать изображение. Это может быть сделано, потому что в телефоне реализовано собственное кэширование всех изображений (обратите внимание на наличие элемента «IgnoreImageCache» в перечислении BitmapCreateOptions).

Это, вероятно, виновник, однако другая возможность заключается в том, что виртуализация ListBox на самом деле не происходит. Наиболее частой причиной этого является то, что элементы в списке не определены явно, чтобы иметь одинаковую высоту. Виртуализация в ListBox использует VirtualizingStackPanel под крышками и требует, чтобы каждый элемент был одинаковой высоты. Если какой-либо элемент имеет другую высоту, поведение виртуализации отменяется. Ниже приведен код, который должен помочь вам определить, является ли virt. на самом деле происходит или нет. Еще одна вещь, связанная с виртуализацией, заключается в том, что в настоящее время ваши изображения не имеют заданной высоты, пока данные изображения не будут загружены. Это означает, что до загрузки изображений все изображения имеют высоту 0 пикселей. Если все изображения имеют высоту 0 пикселей, то это означает, что все они «на виду» в соответствии с вирт. логика, все они должны начать загрузку.

В общем, попробуйте следующее:

  1. Измените CreateOptions на что-то другое (или не устанавливайте его вообще)
  2. Явно задайте высоту тега изображения внутри списка. (это обязательно)
  3. Используйте приведенный ниже код, чтобы проверить, является ли вирт. достигается.

Простая структура для хранения данных изображения:

using System.Diagnostics;

public class BoundImage
{
   private string imageURL;

   public static int TotalImagesRequested = 0;

   public BoundImage(string url)
   {
       imageURL = url;
   }

   public string ImageURL
   {
       get
       {
           TotalImagesRequested++;

           // Watch the output window and see if TotalImagesRequested is 
           // growing to a crazy high amount (if it is it will eventually
           // reach the total Count of the _links variable. But your 
           // app may crash before that happens.
           Debug.WriteLine("Images being requested: " + TotalImagesRequested);
           return imageURL;
       }
   }
}

Измененное свойство для раскрытия ссылок:

//inside MainPage.xaml.cs
public void GetImages(object sender, RoutedEventArgs e) 
{
    (DataContext as ViewModel).GetMeSomeImages();
}

// inside ViewModel.cs
public void GetMeSomeImages() 
{
    List<string> links = ThisMethodGetsLinks();
    Links.Clear();

    _links = new ObservableCollection<BoundImage>();
    foreach(string link in links)
    {
        _links.Add(new BoundImage(link));
    }
}

ObservableCollection<BoundImage> _links;
public ObservableCollection<BoundImage> Links 
{
    get 
    {
        if (_links == null)
            _links = new ObservableCollection<BoundImage>();
        return _links;
    }
    set
    {
        _links = value;
    }
}

Изменено связывание XAML с крюком для свойства ImageURL объекта BoundImage:

// inside MainPage.xaml
<Button Tap="GetImages">Get Images</Button>
  <ListBox ItemSource="{Binding Links}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <!-- I am not binding to Image's Source directly, because I want to use the bitmap's 'Background creation' option and it's download progress event in the future -->
            <Image>
               <Image.Source>
               <BitmapImage UriSource="{Binding ImageURL}" CreateOptions="BackgroundCreation"/>
               </Image.Source>
            </Image>
        </DataTemplate>
    </ListBox.ItemTemplate>
 </ListBox>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...