具有自动内存清理功能的图像下载器

2023-12-11

我有一个项目列表(简单的列表框),其中包含主从基础上的图像(如果用户单击列表项目,则会打开详细信息页面)。我遇到了非常著名的图像内存泄漏问题,描述here, here, here, and here.

一种可能的方法是遍历所有图像当导航自并清洁它们.

In 线程之一,我发现了更有趣的解决方案:它自动清理图像,但这不适用于虚拟化(如果添加用于存储 ImageSource 的私有字段,图像会丢失或混合)。建议的修复是添加依赖属性。

但我仍然面临同样的问题:向下滚动并返回后图像混合在一起。看起来依赖属性是随机更改的,但我无法捕捉到它们更改的时刻。

public class SafePicture : ContentControl
{
    public static readonly DependencyProperty SafePathProperty =
        DependencyProperty.RegisterAttached(
            "SafePath",
            typeof(string),
            typeof(SafePicture),
            new PropertyMetadata(OnSourceWithCustomRefererChanged));

    public string SafePath
    {
        get { return (string)GetValue(SafePathProperty); }
        set { SetValue(SafePathProperty, value); }
    }

    private static void OnSourceWithCustomRefererChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue == null) // New value here
            return;
    }


    public SafePicture()
    {
        Content = new Image();
        Loaded += OnLoaded;
        Unloaded += OnUnloaded;
    }

    private void OnLoaded(object _sender, RoutedEventArgs _routedEventArgs)
    {
        var image = Content as Image;

        if (image == null)
            return;

        var path = (string)GetValue(SafePathProperty); // Also, tried SafePath (debugger cant catch setter and getter calls), but same result.

        image.Source = null;
        {
            var request = WebRequest.Create(path) as HttpWebRequest;
            request.AllowReadStreamBuffering = true;
            request.BeginGetResponse(result =>
            {
                try
                {
                    Stream imageStream = request.EndGetResponse(result).GetResponseStream();
                    DispatcherHelper.CheckBeginInvokeOnUI(() =>
                    {
                        if (imageStream == null)
                        {
                            image.Source = new BitmapImage { UriSource = new Uri(path, UriKind.Relative) };
                            return;
                        }

                        var bitmapImage = new BitmapImage();
                        bitmapImage.CreateOptions = BitmapCreateOptions.BackgroundCreation;
                        bitmapImage.SetSource(imageStream);
                        image.Source = bitmapImage;
                    });
                }
                catch (WebException)
                {
                }
            }, null);
        }
    }


    private void OnUnloaded(object sender, RoutedEventArgs e)
    {
        var image = Content as Image;

        if (image == null)
            return;

        var bitmapImage = image.Source as BitmapImage;
        if (bitmapImage != null)
            bitmapImage.UriSource = null;
        image.Source = null;
    }
}

Usage:

<wpExtensions:SafePicture SafePath="{Binding ImageUrl}"/>

因此,乍一看,它工作正常,但如果向下滚动并向上返回,图像会随机更改。

编辑:在这种情况下,目前,我只使用纯 ListBox,没有虚拟化(但期望在其他情况下)。

EDIT2:重现此问题的示例项目。我相信,它很快就会包含解决方案:https://simca.codeplex.com/


问题是,当使用虚拟化时,用于每个项目的 ui 元素将被回收并重新用于其他对象(以便包括图像对象),并且由于当您滚动得足够快时,您将异步加载图像,因此您将在已被其他项目重复使用的图像。
一个快速修复方法是检查路径值是否仍然相同,如果不是,则返回,因为图像已被另一个对象重用:

...
var request = WebRequest.Create(path) as HttpWebRequest;
        request.AllowReadStreamBuffering = true;
        request.BeginGetResponse(result =>
        {

            try
            {

                Stream imageStream = request.EndGetResponse(result).GetResponseStream();
                DispatcherHelper.CheckBeginInvokeOnUI(() =>
                {
                if (path!=SafePath){
                  //Item has been recycled
                  return;
                }
                 ....

编辑: 代码中存在几个问题: -将RegisterAttached切换到Register,RegisterAttached用于附加属性而不是普通的依赖属性 - 在 OnSourceWithCustomRefererChanged 中调用 OnLoaded,因为 SafePath 属性更改实际上可能在元素加载后发生 -在onLoaded开始处添加清除uri和源,以便在路径为空时清除图像

这是完整的工作代码:

public class SafeImage : ContentControl
{
    private SynchronizationContext uiThread;

    public static readonly DependencyProperty SafePathProperty =
        DependencyProperty.Register("SafePath", typeof (string), typeof (SafeImage),
        new PropertyMetadata(default(string), OnSourceWithCustomRefererChanged));

    public string SafePath
    {
        get { return (string) GetValue(SafePathProperty); }
        set { SetValue(SafePathProperty, value); }
    }


    private static void OnSourceWithCustomRefererChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        SafeImage safeImage = o as SafeImage;
        safeImage.OnLoaded(null, null);
        //OnLoaded(null, null);
        if (e.NewValue == null)
            return;
    }





    public SafeImage()
    {
        Content = new Image();
        uiThread = SynchronizationContext.Current;

        Loaded += OnLoaded;
        Unloaded += OnUnloaded;
    }

    private void OnLoaded(object _sender, RoutedEventArgs _routedEventArgs)
    {
        var image = Content as Image;

        if (image == null)
            return;

        var path = SafePath; //(string)GetValue(SafePathProperty);
        //image.Source = new BitmapImage(new Uri(SafePath));
        Debug.WriteLine(path);

        var bitmapImage = image.Source as BitmapImage;
        if (bitmapImage != null)
            bitmapImage.UriSource = null;
        image.Source = null;

        if (String.IsNullOrEmpty(path))
        {
            //image.Source = new BitmapImage { UriSource = new Uri(Constants.RESOURCE_IMAGE_EMPTY_PRODUCT, UriKind.Relative) };
            return;
        }

        // If local image, just load it (non-local images paths starts with "http")
        if (path.StartsWith("/"))
        {
            image.Source = new BitmapImage { UriSource = new Uri(path, UriKind.Relative) };
            return;
        }



        {
            var request = WebRequest.Create(path) as HttpWebRequest;
            request.AllowReadStreamBuffering = true;
            request.BeginGetResponse(result =>
            {
                try
                {
                    Stream imageStream = request.EndGetResponse(result).GetResponseStream();
                    uiThread.Post(_ =>
                    {

                        if (path != this.SafePath)
                        {
                            return;
                        }
                        if (imageStream == null)
                        {
                            image.Source = new BitmapImage { UriSource = new Uri(path, UriKind.Relative) };
                            return;
                        }

                        bitmapImage = new BitmapImage();
                        bitmapImage.CreateOptions = BitmapCreateOptions.BackgroundCreation;
                        bitmapImage.SetSource(imageStream);
                        image.Source = bitmapImage;
                        //imageCache.Add(path, bitmapImage);
                    }, null);
                }
                catch (WebException)
                {
                    //uiThread.Post(_ =>
                    //{
                    //    image.Source = new BitmapImage { UriSource = new Uri(Constants.RESOURCE_IMAGE_EMPTY_PRODUCT, UriKind.Relative) };
                    //}, null);
                }
            }, null);
        }
    }


    private void OnUnloaded(object sender, RoutedEventArgs e)
    {
        var image = Content as Image;

        if (image == null)
            return;

        var bitmapImage = image.Source as BitmapImage;
        if (bitmapImage != null)
            bitmapImage.UriSource = null;
        image.Source = null;
    }
}

最后一点,Windows Phone ListBox 默认使用虚拟化和回收(使用的 ItemPanel 是 Virtualized StackPanel)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

具有自动内存清理功能的图像下载器 的相关文章

  • HttpResponseMessage 的内容为 JSON

    我有一个 ASP NET MVC WEB API 由于多种原因 由于没有授权而重定向 我不能只使用一个简单的对象并在我的控制器方法中返回它 因此我需要 HttpResponseMessage 类来允许我重定向 目前我正在这样做 var re
  • C 中的分段错误

    我需要用 0 填充二维数组 但编译后的程序会出现此错误 怎么了 int main int vert 1001 1001 int hor 1001 1001 int dudiag 1416 1416 int uddiag 1416 1416
  • 当“”可以分配给std::string时,为什么有“clear”方法?

    一个可以用string clear函数清空字符串 也可以使用空双引号 来执行此操作 有什么不同 当您分配一个空字符串时 编译器必须在数据部分存储一个空的 C 字符串 并创建代码以将指向它的指针传递给赋值运算符 然后 赋值运算符必须从数据部分
  • NUnit 测试运行顺序

    默认情况下 nunit 测试按字母顺序运行 有谁知道有什么方法可以设置执行顺序吗 是否存在这样的属性 我只是想指出 虽然大多数受访者认为这些是单元测试 但问题并没有具体说明它们是 nUnit 是一个很棒的工具 可用于各种测试情况 我可以看到
  • 错误 C2064:术语不计算为采用 1 个参数的函数

    class Student bool Graduate return m bGraduate class School vector
  • 在 C 中声明和初始化数组

    C 有没有办法先声明然后初始化数组 到目前为止 我一直在初始化一个这样的数组 int myArray SIZE 1 2 3 4 但我需要做这样的事情 int myArray SIZE myArray 1 2 3 4 在 C99 中 您可以使
  • 设置外部应用程序焦点

    在 VB NET 中 您可以使用以下命令将焦点设置到外部应用程序 AppActivate Windows Name or AppActivate processID As Integer 现在 如果您这样做 则效果很好 Dim intNot
  • F# 内联如何工作?

    对于 F 我的理解是您可以使用 inline 关键字在调用站点执行类型专门化 那是 val inline a gt b gt c when a or b static member a b gt c 约束条件是 a or b必须有一个静态成
  • 在 DefaultHttpContext 上使用 FeatureCollection 时,响应对象为 null

    我正在测试一些 net Core 中间件 并希望使用整个 asp net Core http 管道来运行中间件 而不是模拟它 问题是 当我使用特征集合时 不知何故 响应对象没有在 httpRequest 中设置 并且它在请求本身上是只读的
  • “volatile void function( ... )” 做了什么?

    我见过从语法角度来看 C 函数中 volatile 关键字有多少种用法 https stackoverflow com questions 7643528 how many usage does volatile keyword have
  • Google Apps 脚本:如何水平对齐 inlineImage

    我有以下代码 它是一个更大程序的一部分 我正在尝试将图像从我的 Google 驱动器插入到 Google 文档中 并调整其大小并居中 到目前为止 我能够让程序插入图像并调整其大小 但我不知道如何使 inlineImage 居中 我是使用谷歌
  • 一些涉及类析构函数和删除运算符的内存管理问题?

    在阅读了一些教程后 我仍然不清楚 C 中内存管理的一些观点 1 当使用 new 运算符声明的类超出范围时 是否会调用其析构函数并释放内存 是否有必要调用删除运算符来释放类的内存并调用其析构函数 class Test void newTest
  • PHP cURL 代理带标头?

    我正在制作一个 PHP 图像代理脚本 我需要它不仅能够回显其请求的图像的内容 而且还能够以相同的方式重现图像请求的标头 我见过一个 另一个 但没有同时看到过 这些 cURL 选项让我感到困惑 我该怎么做 抱歉 我不确定你想要什么 这是从图像
  • 如何最好地为 Visual Studio 2017 构建的 CMake C++ 项目设置输出目录?

    我使用 Visual Studio 2017 使用 vcxproj 文件构建 C 桌面项目 我喜欢默认行为 其中输出目录是项目下面的子目录 例如 myproj sln myproj vcxproj x64 myproj release my
  • 派生类的聚合初始化

    以下代码无法使用 Visual Studio2017 或在线 GDB 进行编译 我期望它能够编译 因为迭代器只是一个具有类型的类 并且它是从公共继承的 这是不允许的还是在 VS2017 中不起作用 template
  • C# 编译器编译 .txt .obj .java 文件

    using System class Program public static void Main Console WriteLine Hello World Console ReadLine 我将文件另存为1 java 2 obj an
  • 序列化时如何跳过 xml 声明?

    我正在尝试输出一个没有 xml 头的 xml 文件 例如 我试过 Type t obj GetType XmlSerializer xs new XmlSerializer t XmlWriter xw XmlWriter Create c
  • MonoGame 中的 ContentLoadException

    我一直在尝试使用 Xamarin Studio 在 MonoGame 中加载纹理 我的代码设置如下 region Using Statements using System using Microsoft Xna Framework usi
  • 即使对于新上下文,OnModelCreating 也仅调用一次

    我有多个相同但内容不同的 SQL Server 表 在编写代码优先 EF6 程序时 我尝试为每个程序重用相同的数据库上下文 并将表名称传递给上下文构造函数 然而 虽然每次都会调用构造函数 但尽管每次都是从 new 创建数据库上下文 但 On
  • 散列 hash_hmac 时,Convert.ToChar(0) 散列结果与 PHP 中的 chr(0) 不同的字符串

    我在 PHP 中有一个字符串 它被转换为字节数组并进行哈希处理 转换为字节数组的字符串如下所示 G 字符 0 便便 我需要 C 中的等效字节数组 这样我才能得到相同的哈希值 编辑 这是完整的问题 生成的哈希值不同 PHP api secre

随机推荐

  • 防止在 Windows 窗体中打开组合框控件的下拉区域

    我在 Windows 窗体中有一个自定义组合框控件 我想实现一个功能 其中根据某些条件不应显示下拉区域 即我需要防止组合框根据某些条件打开 我找到了一个可以实现此目的的链接 但它完全阻止了下拉区域的显示 我也无法根据自己的方便调整该方法 链
  • 通过忽略div标签javascript的内部元素来选择文本

  • pyodbc和ms access 2010连接错误

    如何使用 pyodbc 访问我的 Microsoft Access 2010 数据库 accdb 之前 我使用了 mdb 数据库 它在连接字符串为 ODBC CONN STR DRIVER Microsoft Access Driver m
  • 预引导加载屏幕

    我正在寻找一个预引导加载屏幕 类似于这个例子但对于 Angular 2 来说 我可以建议一种简单的 CSS 方法 首先添加 loadingdiv 到主 HTML 页面中 它应该遵循主应用程序组件元素 例如
  • JavaScript 拖放

    我正在寻找有人解释如何在 javascript 中拖放 我想要一条水平线 其中包含一些可自定义的图像 我看过这些的在线教程 但发现它们很难使用 我建议您研究一下 Javascript 框架之一 我们用原型与剧本 您可以在 Scriptacu
  • 如何在android中加载BufferedImage?

    我要加载BufferedImage在我的应用程序中 为此我正在使用ImageIO但我越来越java lang NoClassDefFoundError BufferedImage tgtImg loadImage ImageD2 jpg p
  • Google Apps 脚本仅在一次迭代后就停止了

    我在谷歌应用程序脚本中有一个小函数 必须在 for 循环中执行一些操作 即我有 3 行 其中包含一些值 对于每一行 我必须更新一张表 但不幸的是 我不明白为什么 但在仅仅一个之后循环程序停止了 并且没有return声明 这是代码 funct
  • 在 SwiftUI 中根据浅色或深色模式更改按钮样式修饰符

    我想设置自定义buttonStyle明暗模式按钮的修饰符 如何根据浅色或深色模式更改buttonStyle Modifier 我想为浅色和深色模式的按钮设置自定义修饰符 这是我的按钮代码 Button action print button
  • 从 R 中的数据框创建词云

    我制作了一个示例数据框 我尝试从 项目 栏制作一个词云 Hours lt c 2 3 4 2 1 1 3 Project lt c a b b a c c c Period lt c 2014 11 22 2014 11 23 2014 1
  • Android ListActivity 基于对象状态的行颜色

    我有一个 ListActivity 显示列表中的一堆对象 我想根据 MonitorObject 中两个布尔值的状态更改行的背景和文本颜色 我需要扩展 ArrayAdapter 吗 如果是这样 代码示例将不胜感激 因为我已经尝试了几天但没有成
  • 带样式的自定义 WPF ListView(使用 DataTemplate) - 如何添加标题?

    我有以下代码 行的数据模板
  • 如何通过批处理文件并行读取两个文本文件?

    有没有一种简单且性能良好的方法来并行读取两个 甚至更多 文本文件 因此 有一个循环在每次迭代中读取每个文本文件的一行 A for F不能使用给定多个文件的循环 因为它会依次读取文件 当然 嵌套的这样的循环也没有意义 诀窍是使用STDIN 重
  • iOS 中的语音输出

    是否可以访问用于辅助功能的 iOS 语音合成功能 这是一个使用的示例AVSpeechSynthesizer在 iOS 7 上 AVSpeechSynthesizer synthesizer AVSpeechSynthesizer alloc
  • Rails 检测请求是否为 AJAX

    在我的操作中 我希望仅在从 AJAX 请求调用时才响应处理 我该如何检查 我想做这样的事情 def action model Model find params id respond to do format if wasAJAXReque
  • Vue Getter & Setter 而不是后端响应中的实际值

    我是 Vue 新手 我很难理解为什么我的问题会发生 当我调用后端来检索一些数据时 响应如下 id Getter Setter name Getter Setter season Getter Setter number Getter Set
  • 使用 OpsHub 工具从本地 TFS 迁移到在线 TFS 速度缓慢

    我们有一个 50GB 的本地 TFS 集合 我们希望将其迁移到在线 TFS 我们现在使用 OpsHub Visual Studio Online Migration Utility 进行测试 一些单独的测试项目的转换似乎工作正常 因此现在我
  • Boost 的 lexical_cast 从双精度到字符串精度

    我正在使用一个不幸使用的库boost lexical cast从 a 转换double to a string 我需要能够明确地反映我这边的行为 但我希望这样做而不传播boost 我可以使用以下方法保证相同的行为吗to string spr
  • 通过 OAuth 对 Google API 进行身份验证调用时遇到问题

    当我尝试使用服务器到服务器身份验证调用 Google Directory API 时 收到错误消息 未授权访问此资源 api 我做了什么 在 Google Developers Console 中创建了一个应用程序 下载私钥并查找服务帐户名
  • 如何使用外部 jar 文件为 java 制作 makefile

    我需要制作一个 makefile 用外部 jar 文件编译和执行我的类 我有4节课 sync java FileSynchroniser java DirectoryTracer java and SyncFileTracer java 我
  • 具有自动内存清理功能的图像下载器

    我有一个项目列表 简单的列表框 其中包含主从基础上的图像 如果用户单击列表项目 则会打开详细信息页面 我遇到了非常著名的图像内存泄漏问题 描述here here here and here 一种可能的方法是遍历所有图像当导航自并清洁它们 I