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
WP_图片管理机制/异步读取网络图片的更多相关文章
- Android 利用 AsyncTask 异步读取网络图片
1.新建Android工程AsyncLoadPicture 新建布局文件activity_main.xml主界面为一个GridView,还有其子项布局文件gridview_item.xml 2.功能主 ...
- tensorflow1.0 队列FIFOQueue管理实现异步读取训练
import tensorflow as tf #模拟异步子线程 存入样本, 主线程 读取样本 # 1. 定义一个队列,1000 Q = tf.FIFOQueue(1000,tf.float32) # ...
- wemall app商城源码中基于JAVA的Android异步加载图片管理器代码
wemall doraemon是Android客户端程序,服务端采用wemall微信商城,不对原商城做任何修改,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可随意定制修改.本文分享其中 ...
- Android图片管理组件(双缓存+异步加载)
转自:http://www.oschina.net/code/snippet_219356_18887?p=3#comments ImageManager2这个类具有异步从网络下载图片,从sd读取本地 ...
- WP_从独立存储区读取缓存的图片
///<summary> /// 独立存储缓存的图片源 /// 用法:item.img = new StorageCachedImage(newUri(http://www.baidu ...
- IOS- 内存管理机制
iOS平台内存常见问题 作为iOS平台的开发者,是否曾经为内存问题而苦恼过?内存莫名的持续增长,程序莫名的crash,难以发现 的内存泄漏,这些都是iOS平台内存相关的常见问题:本文将会详细介绍iOS ...
- Linux 内核的文件 Cache 管理机制介绍
Linux 内核的文件 Cache 管理机制介绍 http://www.ibm.com/developerworks/cn/linux/l-cache/ 1 前言 自从诞生以来,Linux 就被不断完 ...
- 分享一个安卓中异步获取网络图片并自适应大小的第三方程序(来自github)
安卓中获取网络图片,生成缓存 用安卓手机,因为手机流量的限制,所以我们在做应用时,要尽量为用户考虑,尽量少耗点用户的流量,而在应用中网络图片的显示无疑是消耗流量最大的,所以我们可以采取压缩图片或者将图 ...
- java静态类、静态方法、静态代码块,静态变量及实例方法,实例变量初始化顺序及内存管理,机制
1.当一个类被第一次使用时,它需要被类加载器加载,而加载过程涉及以下两点: (1)在加载一个类时,如果它的父类还未被加载,那么其父类必须先被加载: (2)当类加载到内存之后,按照在代码中的出现顺序执行 ...
随机推荐
- All_从PO - INV - AP - SLA - GL重要数据表和接口程式(概念)
2014-07-09 Created By BaoXinjian
- 监控系统一些告警方式对比:短信、Email手机端、IM
一.缘由: 对于运维来说,监控告警是很重要的一环,告警讲究及时性,所以这里讨论下一些常见告警方式的优劣. 二.告警方式: 1.短信SMS 短信网关通知是一种最直接.最有效的通知方式,当然成本最高. 适 ...
- redis 数据持久化
1.快照(snapshots) 缺省情况情况下,Redis把数据快照存放在磁盘上的二进制文件中,文件名为dump.rdb.你可以配置Redis的持久化策略,例如数据集中每N秒钟有超过M次更新,就将数据 ...
- Axis2/c 知识点
官网文档: http://axis.apache.org/axis2/c/core/docs/axis2c_manual.html 从文档中可以总结出: 1. Axis2/C是一个用C语言实现的We ...
- c#unity
using UnityEngine;using System.Collections; public class PacmanMove : MonoBehaviour { public float s ...
- bootstrap-按钮样式
<div class="container"> <!-- 按钮的背景色 --> <div class="row"> < ...
- Jmeter+jenkins接口性能测试平台实践整理(一)
最近两周在研究jmeter+Jenkin的性能测试平台测试dubbo接口,分别尝试使用maven,ant和Shell进行构建,jmeter相关设置略. 一.Jmeter+jenkins+Shell+t ...
- AJAX跨域调用相关知识-CORS和JSONP
1.什么是跨域 跨域问题产生的原因,是由于浏览器的安全机制,JS只能访问与所在页面同一个域(相同协议.域名.端口)的内容. 但是我们项目开发过程中,经常会遇到在一个页面的JS代码中,需要通过AJAX去 ...
- [Flex] PopUpButton系列 —— 将DataGrid作为弹出内容
<?xml version="1.0" encoding="utf-8"?> <!--Flex中如何创建一个可以弹出DataGrid作为菜单的 ...
- Java中this关键字在构造方法中的使用
1. Java中this关键字代表对象本身.用this关键字可以在类的内部调用属性和方法,这样代码的可读性比较高,因为它明确的指出了这个属性或方法的来源. 2. 同时在构造函数中也可以使用this关键 ...