zoukankan      html  css  js  c++  java
  • WP_图片管理机制/异步读取网络图片

    项目有这样的需求,

    要求窗口加载一揽子图片,为了不让UI阻塞太久,采用异步读取后绑定显示的方案.

    图片的下载应该采用并发的过程(等待网络响应会很耗时,一张一张的下载,等待时间太长)

    图片的下载不能占用过多的线程数,应有个阀值(图片不是核心业务,不能占用那么多资源)

    在图片加载的过程中,如果用户有操作,比如窗口跳转,则未加载完成的图片加载的过程应取消(为了替用户节省流量).

    需求就是这么多了,如何实现呢?

    思路是这样的,由于需要异步,且需要等待,首先想到使用队列,先让队列排列起来,再定量迭代读取.

    因为要涉及异步的取消,想到了用WebClient对象的异步功能, 当然,所以发起异步请求之后的对象我都需要记录,

    所以还需要一个list容器.

    外部接口是两个参数,url,图片的网址,一个回调,定义了图片下载完成后的操作.

    内部的核心流程,

    1.将一个图片任务从队列中取出,

    2.异步发生此请求,

    3.将发起请求的对象放进容器,以备撤销时使用.

    撤销的核心流程是.

    1.让处理线程停止

    2.取消队列中的任务,

    3.让等待响应的任务取消.

    using System;
    using System.Windows;
    using System.Windows.Media.Imaging;
    using Proj.Interface;
     
    namespace Proj.Common
    {
        /// <summary>
        /// 把网络数据包装为图片源
        /// </summary>
        public class HttpPicGet : IRevocable
        {
            public event GetPicCallback OnImageLoadCompleted;
            public event Action ProcessCompleted;
     
            /// <summary>
            /// 当前正在处理的URL
            /// </summary>
            public string Url;
     
            HttpResourceGet m_httpGet;
            public HttpPicGet()
            {
                m_httpGet = new HttpResourceGet();
                m_httpGet.OnDataStreamGenerated += (stream =>
                {
                    Deployment.Current.Dispatcher.BeginInvoke(() =>
                    {
                        BitmapSource bi = new BitmapImage();
                        bi.SetSource(stream);
                        if (OnImageLoadCompleted != null)
                        {
                            OnImageLoadCompleted(bi);
                        }
                    });
                });
                m_httpGet.ProcessCompleted += (() =>
                {
                    //Deployment.Current.Dispatcher.BeginInvoke(() =>
                    // {
                         if (ProcessCompleted != null)
                         {
                             ProcessCompleted();
                         }
                     //});
                });
            }
     
            public void BeginLoadPic(string url)
            {
                Url = url;
                m_httpGet.BeginGetData(url);
            }
     
            public void RevokeAsync()
            {
                m_httpGet.RevokeAsync();
            }
        }
    }
    using System;
    using System.IO;
    using System.Net;
    using System.Windows.Media.Imaging;
    using Proj.Interface;
     
    namespace Proj.Common
    {
        /// <summary>
        /// 从网络读取流的回调
        /// </summary>
        public delegate void GetDataStreamCallback(Stream stream);
     
        /// <summary>
        /// 生成了图片源之后的回调
        /// </summary>
        public delegate void GetPicCallback(BitmapSource bimage);
     
        /// <summary>
        /// 获取网络数据
        /// </summary>
        public class HttpResourceGet : IRevocable
        {
            public event GetDataStreamCallback OnDataStreamGenerated;
            public event Action ProcessCompleted;
            WebClient m_client;
     
            public HttpResourceGet()
            {
                m_client = new WebClient();
                m_client.OpenReadCompleted += ((send, ev) =>
                {
                    do
                    {
                        if (ev.Error != null || ev.Cancelled)
                        {
                            break;
                        }
                        if (OnDataStreamGenerated != null)
                        {
                            OnDataStreamGenerated(ev.Result);
                            //ev.Result.Close();
                        }
                    } while (false);
     
                    if (ProcessCompleted != null)
                    {
                        ProcessCompleted();
                    }
                });
            }
     
            public void BeginGetData(string url)
            {
                if (url.Contains("?"))
                {
                    url += "&rand=" + Guid.NewGuid();//加Guid保证调试时无缓存
                }
                else
                {
                    url += "?rand=" + Guid.NewGuid();//加Guid保证调试时无缓存
                }
     
                m_client.OpenReadAsync(new Uri(url));
            }
     
            public void RevokeAsync()
            {
                m_client.CancelAsync();
            } 
        }
     
    }
    using System.ComponentModel;
    using System.Windows.Media.Imaging;
     
    namespace Proj.Common
    {
        public class MyImage : INotifyPropertyChanged
        {
     
            public event PropertyChangedEventHandler PropertyChanged;
            string m_url;
            BitmapSource m_source;
     
            public string URL
            {
                get { return m_url; }
                set
                {
                    if (m_url != value)
                    {
                        m_url = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("URL"));
                    }
                }
            }
     
            public BitmapSource Source
            {
                get { return m_source; }
                set
                {
                    if (m_source != value)
                    {
                        m_source = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Source"));
                    }
                }
            }
     
            protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, args);
            }
        }
    }
    using System.Collections.Generic;
    using System.ComponentModel; 
    using System.Threading; 
    using Proj.Interface;
     
    namespace Proj.Common
    {
        /// <summary>
        /// 容器,用来处理多条任务
        /// </summary>
        public class RevocableContainer
        {
            private class QueueItem
            {
                public GetPicCallback action;
                public string url;
            }
     
            const int Threshold =3;
     
            AutoResetEvent m_event;
            int m_count;
            bool m_isThreadProcessing;
            Queue<QueueItem> m_queue;
            List<IRevocable> m_list;
            object m_lock;
            public RevocableContainer()
            {
                m_event = new AutoResetEvent(false);
                m_queue = new Queue<QueueItem>();
                m_list = new List<IRevocable>();
                m_lock = new object();
                m_count = Threshold;
                m_isThreadProcessing = false;
            }
     
            void HttpRequestThread()
            {
                while (true)
                {
                    if (m_count == 0)
                    {
                        m_event.WaitOne();
                    }
                    QueueItem item = null;
                    //out from queue
                    lock (m_queue)
                    {
                        if (!m_isThreadProcessing)
                        {
                            break;
                        }
                        if (m_queue.Count == 0)
                        {
                            break;
                        }
     
                        item = m_queue.Dequeue();
                        Interlocked.Decrement(ref  m_count);
     
                    }
     
                    //do request
                    HttpPicGet pic = new HttpPicGet();
                    pic.OnImageLoadCompleted += (img =>
                    {
                        item.action(img);
                    });
     
                    pic.ProcessCompleted += (() =>
                    {
                        lock (m_list)
                        {
                            m_list.Remove(pic);
                        }
                        if (m_count == 0)
                        {
                            m_event.Set();
                        }
                        Interlocked.Increment(ref m_count);
                    });
                    pic.BeginLoadPic(item.url);
     
                    //into list
                    lock (m_list)
                    {
                        m_list.Add(pic);
                    }
     
                    Thread.Sleep(1);
                }
            }
     
     
            public void EnQueue(string url, GetPicCallback action)
            {
                QueueItem item = new QueueItem() { action = action, url = url };
                BackgroundWorker worker = null;
                lock (m_queue)
                {
                    m_queue.Enqueue(item);
                    if (!m_isThreadProcessing)
                    {
                        m_isThreadProcessing = true;
                        worker = new BackgroundWorker();
                    }
                }
     
                if (worker != null)
                {
                    worker.DoWork += ((send, ev) => HttpRequestThread());
                    worker.RunWorkerCompleted += ((send, ev) =>
                    {
                        lock (m_queue)
                        {
                            m_isThreadProcessing = false;
                        }
                    });
     
                    worker.RunWorkerAsync();
                }
     
            }
     
            /// <summary>
            /// 取消全部,并返回未完成的(正在进行的及未开始的)
            /// </summary>
            public List<string> CancelAll()
            {
                List<string> unFinishedUrls=new List<string>();
                lock (m_queue)
                {
                    m_isThreadProcessing = false;
                    
                 
                    
                        foreach (var queueItem in m_queue)
                        {
                            unFinishedUrls.Add(queueItem.url);
                        }
                    
     
                    m_queue.Clear();
                }
                lock (m_list)
                {
                    foreach (IRevocable item in m_list)
                    {
                        HttpPicGet picGet = (HttpPicGet)item;
                        unFinishedUrls.Add(picGet.Url);
     
                        item.RevokeAsync();
                    }
                }
     
                return unFinishedUrls;
            }
        }
    }

    界面层:

    using System.Collections.Generic;
    using System.Linq; 
    using System.Windows;
    using Proj.Common;
    using Microsoft.Phone.Controls;
     
    namespace Proj
    {
        public partial class PageImgTest : PhoneApplicationPage
        {
            RevocableContainer m_container = new RevocableContainer();
     
            List<string> _unFinishUrls = new List<string>();
     
            object objLockunFinishUrls = new object();
     
            List<string> sources = new List<string>()
                {
                    //"http://gb.cri.cn/mmsource/images/2008/05/26/ei080526395.jpg",
                    //"http://gb.cri.cn/mmsource/images/2008/05/26/ei080526396.jpg",
                    //"http://gb.cri.cn/mmsource/images/2008/05/26/ei080526397.jpg",
                    //"http://gb.cri.cn/mmsource/images/2008/05/26/ei080526398.jpg",
                    //"http://gb.cri.cn/mmsource/images/2008/05/26/ei080526399.jpg",
                    //"http://gb.cri.cn/mmsource/images/2008/05/26/ei080526400.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526401.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526402.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526403.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526404.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526405.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526406.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526407.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526408.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526409.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526410.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526411.jpg",
                    "http://gb.cri.cn/mmsource/images/2008/05/26/ei080526412.jpg"
                };
     
            // Constructor
            public PageImgTest()
            {
                InitializeComponent();
            }
     
            private void DoClick(object sender, RoutedEventArgs e)
            {
                _isInit = false;
     
                MessageBox.Show("开始重新加载全部");
                StartLoad(sources);
            }
     
            private void RevokeClick(object sender, RoutedEventArgs e)
            {
                if (_isProcessing) return;
     
                List<string> lst = m_container.CancelAll();
     
                MessageBox.Show("取消数:" + lst.Count);
     
                if (lst.Count > 0)
                {
                    var removelst = (from c in lst where _unFinishUrls.Contains(c) select c).ToList();
                    _unFinishUrls.RemoveAll((a) => { return removelst.Contains(a); });
     
                    var addlst = (from c in lst where !_unFinishUrls.Contains(c) select c).ToList();
                    _unFinishUrls.AddRange(addlst);
                }
                else
                    lst = _unFinishUrls.TakeWhile((a) => { return true; }).ToList();
     
                MessageBox.Show("未完成数:" + lst.Count);
            }
     
            private void BtnRetry_OnClick(object sender, RoutedEventArgs e)
            {
                if (!_isInit || lbContent.Items.Count == 0) return;
                //将未完成的图片继续加载
     
                if (_isProcessing) return;
     
                MessageBox.Show("开始处理的图片个数:" + _unFinishUrls.Count);
                StartLoad(_unFinishUrls);
     
            }
     
            private bool _isInit = false;
            private bool _isProcessing = false;
     
            /// <summary>
            /// 加载/继续加载未完成的
            /// </summary> 
            void StartLoad(List<string> argImgUrls)
            {
                if (argImgUrls == null || argImgUrls.Count == 0) return;
     
                if (_isProcessing) return;
                _isProcessing = true;
     
                List<ImageObj> imgs = new List<ImageObj>();
     
                //MyImage[] imgs = new MyImage[sources.Count]; 
                //for (int i = 0; i < argImgUrls.Count; ++i)
                //{
                //    MyImage imgItem = new MyImage();
                //    imgs.Add(imgItem);
                //    //imgs[i] = new MyImage();
                //    //MyImage imgItem = imgs[i];
                //    imgItem.URL = sources[i] + "?rand=" + Guid.NewGuid().ToString();//加Guid保证调试时无缓存
                //    m_container.EnQueue(imgItem.URL, (bitsource => imgItem.Source = bitsource));
                //}
     
                if (!_isInit)
                {
                    for (int i = 0; i < argImgUrls.Count; ++i)
                    {
                        ImageObj imgItem = new ImageObj();
                        imgs.Add(imgItem);
                        imgItem.URL = sources[i];
                        m_container.EnQueue(imgItem.URL, (bitsource =>
                        {
                            imgItem.Source = bitsource;
     
                            if (_unFinishUrls.Contains(imgItem.URL))
                                _unFinishUrls.Remove(imgItem.URL);
                        }));
                    }
     
                    lbContent.DataContext = imgs;
                }
                else
                {
                    for (int i = 0; i < lbContent.Items.Count; i++)
                    {
                        ImageObj myImg = (ImageObj)lbContent.Items[i];
     
                        var item = (from c in argImgUrls where c == myImg.URL select c).FirstOrDefault();
                        if (!string.IsNullOrEmpty(item))//匹配上
                        {
                            m_container.EnQueue(item, (bitsource =>
                            {
                                myImg.Source = bitsource;
     
                                if (_unFinishUrls.Contains(myImg.URL))
                                    _unFinishUrls.Remove(myImg.URL);
                            }));
                        }
                    }
                }
     
                if (!_isInit && imgs.Count > 0)
                {
                    _isInit = true;
                }
     
                _isProcessing = false;
                UpdateLayout();
     
            }
     
        }
    }
     
     
     
     
    <phone:PhoneApplicationPage
        x:Class="Proj.PageImgTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
        xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        SupportedOrientations="Portrait" Orientation="Portrait"
        mc:Ignorable="d"
        shell:SystemTray.IsVisible="True">
     
        <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
     
            <ListBox Height="670" x:Name="lbContent" Grid.Row="0" ItemsSource="{Binding}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <Image Height="100" Width="100" Source="{Binding Source, Mode=OneWay}" />
                            <TextBlock Text="{Binding URL}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
     
            <!--ContentPanel - place additional content here-->
            <Grid x:Name="ContentPanel" Grid.Row="1" VerticalAlignment="Bottom" Margin="12,0,12,0">
                <StackPanel Orientation="Horizontal">
                    <Button x:Name="btnDo" Width="100" Height="100" Content="DO" Click="DoClick" />
                    <Button x:Name="btnRevoke" Width="100" Height="100" Content="Revoke" Click="RevokeClick"  />
                    <Button x:Name="btnRetry" Width="120" Height="100" Content="Retry" Click="BtnRetry_OnClick" />
                </StackPanel>
            </Grid>
        </Grid>
     
    </phone:PhoneApplicationPage>

    参考地址:http://blog.csdn.net/antsnm/article/details/6738292

  • 相关阅读:
    django 之(一) --- DjangoRestFormwork
    工具 --- Git理解
    部署 --- Docker使用
    django 之(三) --- 会话|关系|静态*
    flask 之(七) --- 认证|文件|部署
    系统 --- Linux系统环境搭建
    flask 之(六) --- API|RestfulApi
    flask 之(五) --- 对象|钩子|拆分
    数据类型检测
    基本数据类型和引入数据类型
  • 原文地址:https://www.cnblogs.com/jx270/p/3915787.html
Copyright © 2011-2022 走看看