windows phone7 下 Silverlight 异步读取网络图片

项目有这样的需求,

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

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

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

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

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

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

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

所以还需要一个list容器.

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

内部的核心流程,

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

2.异步发生此请求,

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

撤销的核心流程是.

1.让处理线程停止

2.取消队列中的任务,

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

需要用到以下命名空间:

  
  
using System; using System.Collections.Generic; using System.Net; using System.Windows; using Microsoft.Phone.Controls; using System.Windows.Media.Imaging; using System.IO; using System.ComponentModel; using System.Threading;

先声明两个回调

  
  
public delegate void GetDataStreamCallback(Stream stream); public delegate void GetPicCallback(BitmapSource bimage);

第一个是从网络读取流的回调,第二个是生成了图片源之后的回调.

定义了一个接口:

  
  
/// <summary> /// 可以撤销的操作 /// </summary> interface IRevocable { void RevokeAsync(); event Action ProcessCompleted; }

一个方法,是用来撤销操作的,

一个事件,是当异步完成时触发的.(不管是正常完成还是撤销,都视为操作完成)

做一个类继承该接口,用来获取网络数据

  
  
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) { m_client.OpenReadAsync( new Uri(url)); } public void RevokeAsync() { m_client.CancelAsync(); } }

再做一个类,把网络数据包装为图片源

  
  
public class HttpPicGet : IRevocable { public event GetPicCallback OnImageLoadCompleted; public event Action ProcessCompleted; HttpResourceGet m_httpGet; public HttpPicGet() { m_httpGet = new HttpResourceGet(); m_httpGet.OnDataStreamGenerated += (stream => { BitmapSource bi = new BitmapImage(); bi.SetSource(stream); if (OnImageLoadCompleted != null ) { OnImageLoadCompleted(bi); } }); m_httpGet.ProcessCompleted += (() => { if (ProcessCompleted != null ) { ProcessCompleted(); } }); } public void BeginLoadPic( string url) { m_httpGet.BeginGetData(url); } public void RevokeAsync() { m_httpGet.RevokeAsync(); } }

做一个容器,用来处理多条任务

  
  
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(); } } public void CancelAll() { lock (m_queue) { m_isThreadProcessing = false ; m_queue.Clear(); } lock (m_list) { foreach (IRevocable item in m_list) { item.RevokeAsync(); } } } }

做异步绑定需要的类

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

业务的部分都做完了

接下来就是做一个例子来验证成果了

  
  
public partial class MainPage : PhoneApplicationPage { RevocableContainer m_container = new RevocableContainer(); // Constructor public MainPage() { InitializeComponent(); } private void DoClick( object sender, RoutedEventArgs e) { string [] sources = new 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 " }; MyImage[] imgs = new MyImage[sources.Length]; for ( int i = 0 ; i < imgs.Length; ++ i) { 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)); } lbContent.DataContext = imgs; } private void RevokeClick( object sender, RoutedEventArgs e) { m_container.CancelAll(); } }
XAML页面代码如下:

  
  
<!-- 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" /> </ StackPanel > </ Grid > </ Grid >

运行起来,达到需要所要求的效果

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值