Потребление памяти BitmapImage/Image control в Windows Phone 8
Я тестирую приложение WP8, которое позволяет просматривать много изображений. Я обнаружил, что потребление памяти приложения растет, и хочу выяснить, как его решить.
Я читал некоторые статьи из Интернета, однако решения, предоставленные этими статьями, не работают в моем приложении, пожалуйста, прочтите историю ниже.
Сначала я нашел статью " Советы по работе с изображениями для Windows Phone 7" и загрузил ее образец, чтобы выполнить чистое тестирование кэша изображений, он работает с одним изображением.
А затем для целей тестирования я создаю это приложение из 15 автономных изображений внутри приложения и задаю его как "Контент". Загрузите тестовое приложение отсюда.
Мои шаги тестирования:
(1) Launch app
(2) Go to Image Caching page
(3) Enable checkbox "Avoid Image Caching"
(4) Continuously tapping button Show/Clear
(5) Keep watching the memory status textblock at the bottom
Когда я тестирую свое приложение, память увеличивается, например, 16,02 МБ => Показать (19,32 МБ) => Очистить (16,15 МБ) => Показать (20,18 МБ) => Очистить (17,03 МБ)... и т. Д. И память не будет освобожден, даже если он покинет страницу кэширования и снова перейдет на страницу кэширования. Похоже, решение статьи " Image Tips for Windows Phone 7" работает только для 1 изображения.
Здесь представлены xaml и кодовый код решения " Советы по изображениям для Windows Phone 7".
[Caching.xaml]
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<ToggleButton Content="Show" Width="150" Checked="ShowImageClicked" Unchecked="ClearImageClicked"/>
<CheckBox x:Name="cbAvoidCache" Content="Avoid Image Caching"/>
</StackPanel>
<Image x:Name="img" Grid.Row="2" Width="256" Height="192"/>
<TextBlock x:Name="tbMemory" Grid.Row="2" Text="Memory: " VerticalAlignment="Bottom" Style="{StaticResource PhoneTextLargeStyle}"/>
</Grid>
[Caching.xaml.cs]
public partial class Caching : PhoneApplicationPage
{
public Caching()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Start();
timer.Tick += delegate
{
GC.Collect();
tbMemory.Text = string.Format("Memory: {0} bytes", DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage"));
};
}
private int nIndex = 1;
BitmapImage bitmapImageFromUri = new BitmapImage();
private void ShowImageClicked(object sender, RoutedEventArgs e)
{
string strImage = string.Format("../ImagesAsContent/{0:D2}.jpg", nIndex);
bitmapImageFromUri.UriSource = new Uri(strImage, UriKind.Relative);
img.Source = bitmapImageFromUri;
nIndex++;
if (nIndex > 15)
{
nIndex = 1;
}
(sender as ToggleButton).Content = "Clear";
}
private void ClearImageClicked(object sender, RoutedEventArgs e)
{
if (cbAvoidCache.IsChecked == true)
{
// set the UriSource to null in order to delete the image cache
BitmapImage bitmapImageFromUri = img.Source as BitmapImage;
bitmapImageFromUri.UriSource = null;
}
img.Source = null;
(sender as ToggleButton).Content = "Show";
}
}
Я также пытался искать любые другие решения, некоторые результаты тестирования, как показано ниже.
(1) Статья " [wpdev] Утечка памяти с BitmapImage": она предоставляет 2 решения, одно - API DisposeImage, другое - установить для источника BitmapImage значение null, как показано ниже. Также в статье сообщается, что мы должны быть осторожны с присоединением / извлечением обработчика событий, однако мое тестирующее приложение не имеет обработчика событий на странице кэширования.
[DisposeImage]
private void DisposeImage(BitmapImage image)
{
if (image != null)
{
try
{
using (var ms = new MemoryStream(new byte[] { 0x0 }))
{
image.SetSource(ms);
}
}
catch (Exception)
{
}
}
}
[Установить ноль]
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
(2) Статья " Windows phone: список с изображениями вне памяти": предоставляет API "DisposeImage" с небольшой разницей, чем (1), как показано ниже, но это также не работает, я все еще получил память симптомы
public static void DisposeImage(BitmapImage image)
{
Uri uri= new Uri("oneXone.png", UriKind.Relative);
StreamResourceInfo sr=Application.GetResourceStream(uri);
try
{
using (Stream stream=sr.Stream)
{
image.DecodePixelWidth=1; //This is essential!
image.SetSource(stream);
}
}
catch
{}
}
(3) Статья " Не удается найти утечку памяти": в ней представлены те же 2 решения, что и выше, также упоминается, что проблема не может быть воспроизведена для образов изолированного хранилища, однако образы моего тестирующего приложения взяты из изолированного хранилища.
(4) Я также пытался получить 1000 изображений, результатом тестирования является сбой приложения, когда приложение последовательно показывало около 190 изображений, см. Ниже таблицу памяти для анализа приложений Windows Phone.
Наконец, спасибо за ваше терпение, чтобы прочитать мой вопрос и историю, я работал над этим, чтобы найти решение в течение многих дней. Если у вас есть какие-либо подсказки или решения, пожалуйста, дайте мне знать.
Благодарю.
1 ответ
Я имел дело с той же проблемой, и я думаю, в конце концов, что на самом деле я нашел обходной путь, я не профессиональный программист, но вот мое решение:
public Task ReleaseSingleImageMemoryTask(MyImage myImage, object control)
{
Pivot myPivot = control as Pivot;
Task t = Task.Factory.StartNew(() =>
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (myImage.img.UriSource != null)
{
myImage.img.UriSource = null;
DisposeImage(myImage.img);
}
PivotItem it = (PivotItem)(myPivot.ItemContainerGenerator.ContainerFromIndex(myImage.number % 10));
Image img = FindFirstElementInVisualTree<Image>(it);
if (img != null)
{
img.Source = null;
GC.Collect();
}
});
myImage.released = true;
});
return t;
}
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
{
return (T)child;
}
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
private void DisposeImage(BitmapImage img)
{
if (img != null)
{
try
{
using (var ms = new MemoryStream(new byte[] { 0x0 }))
{
img = new BitmapImage();
img.SetSource(ms);
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("ImageDispose FAILED " + e.Message);
}
}
}
Надеюсь это поможет:)