OutOfMemoryException @ WriteableBitmap @后台代理

Max*_*eng 7 c# writeablebitmap windows-phone-8

我有一个Windows Phone 8应用程序,它使用后台代理:

  1. 从互联网获取数据;
  2. 基于用户控件生成图像,该用户控件使用步骤1中的数据作为数据源;
  3. 在用户控件中,我有Grid&StackPanel和一些文本和图像控件;
  4. 当某些图像使用安装文件夹中的本地资源(/ Assets/images/...)时,用户从手机的照片库中选择了我用作背景的其中一个,因此我必须使用C#设置源代码背后.

但是,当它在后台运行时,它会得到OutOfMemoryException一些故障排除:

  1. 当我在"前线"中运行该过程时,一切正常;
  2. 如果我注释掉更新进度,并直接创建图像,它也可以正常工作;
  3. 如果我没有设置背景图像,它也可以正常工作;
  4. OutOfMemoryException在期间抛出var bmp = new WriteableBitmap(480, 800);
    我已经缩水为1280*768到800*480的图像大小,我认为这是一个全屏幕的背景图像的底线,不是吗?
  5. 经过一些研究,我发现出现这个问题是因为它超过了周期性任务的11 MB限制.
  6. 我试过DeviceStatus.ApplicationCurrentMemoryUsage用来跟踪内存使用情况:

    - 限制是11,534,336(位)

    - 当后台代理启动时,即使没有任何任务,内存使用量也变为4,648,960

    - 从互联网获得更新时,它增长到5,079,040

    - 完成后,它回落到4,648,960

    - 当调用开始时(从用户控件生成图像),它增长到8,499,200

嗯,我想这就是问题所在,它几乎没有可用于渲染图像的内存WriteableBitmap.

知道如何解决这个问题吗?

有没有更好的方法从用户控件/或其他任何东西生成图像?

实际上原始图像可能只有100 kb左右,但是,在渲染时WriteableBitmap,文件大小(以及我猜想的所需内存大小)可能会增长到1-2MB.

或者我可以从任何地方释放内存吗?

================================================== ============

顺便说一下,当这个Code Project文章说我在周期性任务中只能使用11MB内存时;

但是,这篇MSDN文章说我可以使用Windows Phone 8 Update 3最多20 MB或25 MB; 哪个是对的?为什么我处于第一种情况?

================================================== ============

编辑:

说到调试器,它在MSDN文章中也有说明:

When running under the debugger, memory and timeout restrictions are suspended.

但为什么我仍然会遇到限制?

================================================== ============

编辑:

好吧,我发现似乎有些帮助,我现在就检查一下,建议仍然受欢迎.

http://writeablebitmapex.codeplex.com/

http://suchan.cz/2012/07/pro-live-tiles-for-windows-phone/

http://notebookheavy.com/2011/12/06/microsoft-style-dynamic-tiles-for-windows-phone-mango/

================================================== ============

生成图像的代码:

Deployment.Current.Dispatcher.BeginInvoke(() =>
{
    var customBG = new ImageUserControl();
    customBG.Measure(new Size(480, 800));
    var bmp = new WriteableBitmap(480, 800); //Thrown the **OutOfMemoryException**
    bmp.Render(customBG, null);
    bmp.Invalidate();
    using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
    {
        filename = "/Shared/NewBackGround.jpg";
        using (var stream = isf.OpenFile(filename, System.IO.FileMode.OpenOrCreate))
        {
            bmp.SaveJpeg(stream, 480, 800, 0, 100);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下的XAML代码ImageUserControl:

<UserControl blabla... d:DesignHeight="800" d:DesignWidth="480">
    <Grid x:Name="LayoutRoot">
    <Image x:Name="nBackgroundSource" Stretch="UniformToFill"/>

    //blabla...
    </Grid>
</UserControl>
Run Code Online (Sandbox Code Playgroud)

C#代码背后ImageUserControl:

public ImageUserControl()
{
    InitializeComponent();
    LupdateUI();
}

public void LupdateUI()
{
    DataInfo _dataInfo = new DataInfo();
    LayoutRoot.DataContext = _dataInfo;
    try
    {
        using (var isoStore = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (var isoFileStream = isoStore.OpenFile("/Shared/BackgroundImage.jpg", FileMode.Open, FileAccess.Read))
            {
                BitmapImage bi = new BitmapImage();
                bi.SetSource(isoFileStream);
                nBackgroundSource.Source = bi;
            }
        }
    }
    catch (Exception) { }
}
Run Code Online (Sandbox Code Playgroud)

如果DataInfo是内另一类设置页面持有从互联网上得到的数据:

public class DataInfo
{
    public string Wind1 { get { return GetValueOrDefault<string>("Wind1", "N/A"); } set { if (AddOrUpdateValue("Wind1", value)) { Save(); } } }
    public string Wind2 { get { return GetValueOrDefault<string>("Wind2", "N/A"); } set { if (AddOrUpdateValue("Wind2", value)) { Save(); } } }
    //blabla...
}
Run Code Online (Sandbox Code Playgroud)

Kev*_*sse 1

If I comment out the update progress, and create the image directly, it also works fine我认为你应该专注于那部分。似乎表明更新后某些内存未释放。在渲染图片之前,请确保更新过程中使用的所有引用都超出范围。强制垃圾收集也有帮助:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(); // Frees the memory that was used by the finalizers
Run Code Online (Sandbox Code Playgroud)

另一件需要考虑的事情是调试器也使用大量内存。通过在“发布”模式下编译项目并在手机上部署来进行实际测试,以确保内存不足。

不过,我已经处于这种情况,所以我知道这可能还不够。要点是:.NET Framework 中的某些库是延迟加载的。例如,如果您的更新过程涉及下载一些数据,那么后台代理将加载网络库。这些库无法卸载,并且会浪费代理的一些内存。这就是为什么,即使释放更新过程中使用的所有内存,也无法恢复启动后台代理时所拥有的相同数量的可用内存。看到这一点,我在我的一个应用程序中所做的是将后台代理的工作负载跨越两次执行。基本上,当代理执行时:

  • 检查隔离存储中是否有待处理的数据。如果没有,只需执行更新过程并将所有需要的数据存储在隔离存储中
  • 如果有待处理的数据(即下次执行时),则生成图片并清除数据

这意味着图片每小时仅生成一次,而不是每 30 分钟生成一次,因此仅当其他一切都失败时才使用此解决方法。