图像工具 动画图像不释放内存
本文关键字:图像 内存 释放 动画 工具 | 更新日期: 2023-09-27 18:30:37
我有一个WindowsPhone 7.8应用程序,它需要显示动画GIF以及Silverlight直接支持的图像。因此,我们创建了一个自定义用户控件public sealed partial class ExtendedImageControl : UserControl, IDisposable
,它具有一个名为ImageSource的依赖属性。此属性绑定到 URL。然后,隐藏的代码会将常规 Image 控件插入到 LayoutRoot 或 AnimatedImage 中。但是,当 AnimatedImage 退出视图或包含页面关闭时,它不会释放其内存。
加载逻辑如下:
ExtendedImage loadedImage = (ExtendedImage)await Utils.Caching.Image.LoadCachedImageFromUrlAsync<ExtendedImage>(loadedLocation);
if (ImageSource == loadedLocation)
{
AnimatedImage image = new AnimatedImage();
image.Stretch = stretch;
image.Source = loadedImage;
LayoutRoot.Children.Add(image);
CurrentImageMode = ExtendedImageMode.AnimatedImage;
loadedImage = null;
#if DEBUG
App.logger.log("Loaded {0} as animated image", loadedLocation);
#endif
imageDisplay = image;
raiseImageUpdated();
}
最终,图像加载
WebClient client = new WebClient();
ExtendedImage image = new ExtendedImage();
using (Stream source = await client.OpenReadTaskAsync(location))
{
if (location.ToString().EndsWith("gif", StringComparison.InvariantCultureIgnoreCase))
{
image.SetSource(source);
TaskCompletionSource<ExtendedImage> imageLoaded = new TaskCompletionSource<ExtendedImage>();
EventHandler loadingCompleteHandler = new EventHandler((sender, e) =>
{
imageLoaded.SetResult(image);
});
EventHandler<UnhandledExceptionEventArgs> loadingFailedHandler = new EventHandler<UnhandledExceptionEventArgs>((sender, e) =>
{
imageLoaded.SetResult(image);
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Break();
#endif
});
image.LoadingCompleted += loadingCompleteHandler;
image.LoadingFailed += loadingFailedHandler;
image = await imageLoaded.Task;
//Remove handlers, otherwise the object might be kept in the memory
image.LoadingCompleted -= loadingCompleteHandler;
image.LoadingFailed -= loadingFailedHandler;
}
else
{
//... load with native Silverlight methods
}
}
return image;
我们很早就注意到了内存问题,因此该控件实现了 IDisposable 接口
public void Dispose()
{
unloadImage();
GC.SuppressFinalize(this);
}
private void unloadImage()
{
SmartDispatcher.BeginInvoke(() =>
{
if (imageDisplay != null)
{
if (imageDisplay is AnimatedImage)
{
if ((imageDisplay as AnimatedImage).Source != null & (imageDisplay as AnimatedImage).Source.Frames != null)
(imageDisplay as AnimatedImage).Source.Frames.Clear();
(imageDisplay as AnimatedImage).Stop();
(imageDisplay as AnimatedImage).Source = null;
}
else if (imageDisplay is Image && ((Image)imageDisplay).Source != GIFplaceholder)
{
(imageDisplay as Image).Source = null;
}
imageDisplay = null;
}
});
}
但是,永远不会在图像上调用 Dispose 方法。
我该怎么做才能找出为什么 GC 没有拾取此对象?我没有注册任何事件处理程序,因此据我了解,应该在导航离开应用程序页面时收集它。我也尝试添加一个析构函数,但是这个也没有被调用。
内存泄漏不在 ImageTools 中,但实际上是 Silverlight 运行时中的一个错误:
动态添加和删除图像时内存泄漏
解决办法:在动态添加或移除位图图像时 应用程序(又名从树中添加/删除),您应该设置 Image.Source = null,然后从树中删除 Image 元素。 这将使位图图像符合垃圾回收的条件。错误 状态:活动错误。*
引用:
Silverlight:如何从内存中卸载(处置)图像?https://blogs.msdn.microsoft.com/silverlight_sdk/2008/10/28/silverlight-bugs-and-workarounds/
可以快速验证在向后导航离开 XAML 页面之前将每个扩展映像设置为"无"是否可以显著减少应用程序内存使用量。
我将以下内容添加到 App.xaml 中的RootFrame_Navigating事件中,以帮助跟踪这些泄漏:
Dim applicationCurrentMemoryUsage As Long = Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage")
Dim applicationPeakMemoryUsage As Long = Microsoft.Phone.Info.DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage")
Debug.WriteLine(DateTime.Now.ToLongTimeString() & " Current : " & applicationCurrentMemoryUsage & " Peak : " & applicationPeakMemoryUsage)
当应用程序页面导航离开时,应收集它
将代码放入导航过程中以实际调用 dispose 并取消图像与页面的链接。保存图像的任何内容可能不会被处理,因此图像永远不会被处置。手动完成,然后查看是否发生相同的情况。
OP报告说,关于动画图像的一些事情仍然是一个问题。确保没有将映像pinning
到内存的订阅。必须通过-=
取消订阅流程删除所有订阅。否则,即使没有对对象的引用,任何订阅也会将其固定到内存中,使其保持活动状态。
如果有人偶然发现这个...
有关解决方案,请参阅此处。
简短回答:在导航离开之前调用 AnimatedImage 上的 Stop()。
编辑:我已经更新了库,以便在从页面中删除控件时自动停止动画。下载并构建最新版本的源代码。