图片下载,支持自动记忆清洗 [英] Image downloader with auto memory cleaning

查看:109
本文介绍了图片下载,支持自动记忆清洗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有在主 - 详细信息基础映像的项目列表(简单列表框)(如果用户点击列表项,详细信息页面被打开)。我这里面临着图像内存泄漏颇负盛名的问题,说明,< A HREF =htt​​p://stackoverflow.com/questions/13816569/windows-phone-listbox-with-images-out-of-memory?lq=1>这里,的here 和的此处

I have a list (simple ListBox) of items with images on master-detail base (if user clicks on list item, details page is opened). I faced quite famous problem with images memory leaks, described here, here, here, and here.

一种可能的方式是对通过所有图像的时候NavigatingFrom 和清洁他们

One possible way is to run through all images when NavigatingFrom and clean them.

,我发现更有趣的解决方案的主题之一:它会自动清除的图像,但不工作的虚拟化(图像丢失或混合,如果增加私有字段存储的ImageSource)。建议的修复方法是添加依赖项属性。

In one of the threads, i found more interesting solution: it cleans images automatically, but that is not working for virtualization (images are lost or mixed, if to add private field for storing ImageSource). Suggested fix was to add dependency property.

但我仍然面临同样的问题:图像向下滚动,并返回后混了。 。看上去就像依赖属性的随机变化,但我不能赶上时,他们正在改变的时刻。

But i'm still facing the same problem: images are mixed up after scrolling down and returning up. Looking like dependency property are changed randomly, but i cant catch the moment when they are changing.

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}"/>



所以,乍一看,它工作正常,但如果向下滚动,并返回后,图像。随意变更

So, at first glance, it works fine, but if to scroll down and return up, images are changed randomly.

编辑:在这种情况下,现在,我使用的唯一的纯列表框,没有虚拟化(但在其他情况下,期待它)

in this case, for now, i'm using only pure ListBox, without virtualization (but expecting it in other cases).

EDIT2:示例项目重现此问题。我相信,它将包含在一段时间的解决方案: https://simca.codeplex.com/

sample project to reproduce this problem. I believe, it would contain solution in a while: https://simca.codeplex.com/

推荐答案

的问题是,使用虚拟化时,用于每个产品的UI元素被回收和再利用于其他目的(以便包括所述图像对象),因为你是异步加载图像时您滚动速度不够快,你将被设置在其上已经被重新用于另一个项目图像的位图。结果
速战速决的仅仅是检查路径值仍然是相同的,如果不是这样,只是返回因为图像已经被另一个对象重用:

The problem is that when using virtualisation, the ui element used for each item are recycled and reused for other object (so that include the image object) and since you are loading the image asynchronously when you scroll fast enough your will be setting a Bitmap on an Image which are already been reused for another item.
A quick fix for is just to check that the path value is still the same and if it is not, just return since the image has already been reused for another object:

...
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;
                }
                 ....

编辑:
有一个在代码中的几个问题:
-SWITCH RegisterAttached注册,RegisterAttached是附加属性不正常的依赖项属性
-call在OnSourceWithCustomRefererChanged OnLoaded因为SafePath属性改变元素之后实际上可以happpen被载入
- 增加清晰的URI和源在onLoaded的开始,使其清晰的图像时,路径是空的。

There were a several problem in the code: -Switch RegisterAttached to Register, RegisterAttached is for attached property not normal dependency property -call OnLoaded in OnSourceWithCustomRefererChanged because the SafePath property changed can actually happpen after the element is loaded -Add clear uri and source at the start of onLoaded so that it clear image when path is empty

下面是一个完整的工作代码:

Here is a full working code :

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列表框使用虚拟化和回收利用缺省情况下(ItemPanel采用的是VirtualisedStackPanel)。

Just as a final note, the windows phone ListBox is using virtualisation and recycling by default (the ItemPanel used is a VirtualisedStackPanel).

这篇关于图片下载,支持自动记忆清洗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆