在上篇中,我们实现了一个带有池化、并发连接限制能力的资源下载器,虽然这个下载器能够使我们的游戏动态加载资源并在加载完毕/失败后调用回调方法。但是还是缺乏一些智能。比如我要加载一个game_role_petegg.zip的资源包,如我们有需求:
1.对已经下载过的资源不再重复下载。
2.下载过的资源要缓存,一方面存在内存中,一方面存在isolatedstorage中。
实现这些功能会极大的提高用户体验,所我们有必要设计一个带有缓存控制能力的资源管理器。
复用已有的DownloadDelegater
搞开发的嘛,不可能每一个需求都单独写一片代码,这要将对象的职责分离清楚,复用还是很容易的。
我们定义需求接口:
public
interface
IWebClientResourceService
{
void
Load(Uri uri, DownloadResultEventHandler callback);
void
PutIsolatedStorageToMemoryCache(Uri uri);
void
PutMemoryCacheToIsolatedStorage(Uri uri);
void
RemoveCache(Uri uri);
}
Load方法会下载指定uri的资源,如果之前已经在下载过就不会重复下载。
PutIsolatedStorageToMemoryCache将隔离存储中的数据加载到内存中。
PutMemoryCacheToIsolatedStorage将内存数据保存到隔离存储。
下面通过组合的方式复用之前的DownloadDelegater。

WebClientResourceService
namespace
Sopaco.Silverlight.Insfrastructure.Net.DownloadActivator
{
public
class
WebClientResourceService : Sopaco.Silverlight.Insfrastructure.Net.DownloadActivator.IWebClientResourceService
{
public
static
readonly
WebClientResourceService Instance;
static
WebClientResourceService()
{
Instance
=
new
WebClientResourceService();
}
private
Dictionary
<
Uri, DownloadTask
>
_resourceCacher
=
new
Dictionary
<
Uri, DownloadTask
>
();
private
DownloadDelegater _downloadDelegater
=
new
DownloadDelegater();
private
object
_locker_ResourceCacher
=
new
object
();
public
void
Load(Uri uri, DownloadResultEventHandler callback)
{
#region
检查是否已经有缓存
lock
(_locker_ResourceCacher)
{
if
(_resourceCacher.ContainsKey(uri))
{
if
(callback
!=
null
)
{
var task
=
_resourceCacher[uri];
callback(task.CloneStream, task.Status);
}
return
;
}
_resourceCacher[uri]
=
new
DownloadTask()
{
Uri
=
uri,
Stream
=
null
,
Status
=
DownloadStatus.UnStarted
};
}
#endregion
#region
使用下载器下载
_downloadDelegater.Download(uri, (stream, status)
=>
{
onResultArrive(uri, status, stream);
if
(callback
!=
null
)
callback(stream, status);
});
if
(_resourceCacher[uri].Status
==
DownloadStatus.UnStarted)
//
防止过快下载而使状态不同步
{
_resourceCacher[uri].Status
=
DownloadStatus.Running;
}
#endregion
}
///
<summary>
///
移除缓存
///
未防止缓存内容过大导致内存持续消耗,应该适当调用此方法移出缓存(或者存放在隔离存储中?)
///
</summary>
public
void
RemoveCache(Uri uri)
{
lock
(_locker_ResourceCacher)
{
if
(_resourceCacher.ContainsKey(uri))
{
var cacheObj
=
_resourceCacher[uri];
if
(_resourceCacher.Remove(uri))
{
cacheObj.Dispose();
}
else
{
System.Diagnostics.Debug.WriteLine(
"
移除资源异常:无法移除资源@
"
+
System.Reflection.MethodInfo.GetCurrentMethod().ToString());
}
}
}
}
///
<summary>
///
将一个资源缓存到隔离存储中
///
</summary>
///
<param name="uri"></param>
public
void
PutMemoryCacheToIsolatedStorage(Uri uri)
{
if
(_resourceCacher.ContainsKey(uri))
{
IsolateStorageRespository.Instance.AddWithOverwrite(uri.ToString(), _resourceCacher[uri]);
}
}
///
<summary>
///
从隔离存储中将一个资源缓存起来
///
</summary>
///
<param name="uri"></param>
public
void
PutIsolatedStorageToMemoryCache(Uri uri)
{
if
(_resourceCacher.ContainsKey(uri))
{
var cache
=
IsolateStorageRespository.Instance.Get
<
DownloadTask
>
(uri.ToString());
if
(cache
!=
null
)
{
_resourceCacher[uri]
=
cache;
}
}
}
private
void
onResultArrive(Uri uri, DownloadStatus status, MemoryStream stream)
{
_resourceCacher[uri].Status
=
status;
_resourceCacher[uri].Stream
=
stream;
}
}
}
进一步使用
上面的WebClientResourceService功能还是有些“大一统”,我们可以进一步针对一些特定资源进行应用。
示例需求:对于图片资源包和一些零散的没有放到资源包里的图片资源,一旦下载下来将其放到隔离存储中,下次再需要下载此图片时就直接从隔离存储中取得(如果资源有更新的话将隔离存储中的数据清除就ok)。
GameImageResService
namespace
Sopaco.Silverlight.GameFramewrok.GameRes
{
///
<summary>
///
应用WebClientResourceService定制的一个游戏资源服务模块
///
</summary>
public
class
GameImageResService
{
#region
make it a singleton
public
static
readonly
GameImageResService Instance;
static
GameImageResService()
{
Instance
=
new
GameImageResService();
}
public
GameImageResService()
{
}
#endregion
private
static
readonly
string
IMG_ZIP_XML
=
"
resconfig.xml
"
;
private
WebClientResourceService _resourceService
=
WebClientResourceService.Instance;
private
Dictionary
<
Uri, BitmapImage
>
_imageCache
=
new
Dictionary
<
Uri, BitmapImage
>
();
private
Dictionary
<
string
, BitmapImage
>
_namedImageCache
=
new
Dictionary
<
string
, BitmapImage
>
();
///
<summary>
///
图片资源
///
</summary>
public
void
GetBitmapImage(Uri uri, Action
<
BitmapImage
>
callback)
{
if
(_imageCache.ContainsKey(uri))
{
callback(_imageCache[uri]);
return
;
}
_resourceService.Load(uri, (stream, status)
=>
{
if
(status
==
DownloadStatus.Completed)
{
var image
=
new
BitmapImage();
image.SetSource(stream);
_imageCache[uri]
=
image;
_resourceService.PutMemoryCacheToIsolatedStorage(uri);
//
图片资源由GameResService托管,将WebClientResourceService中的缓存持久化
callback.ExecuteSecurity(image);
}
else
{
#if
DEBUG
throw
new
Exception(
"
error in resource downloading
"
);
#endif
}
});
}
///
<summary>
///
载入一个zip资源包中的图片资源
///
</summary>
/*
配置文件格式
* resconfig.xml
* <?……?>
* <AppRoot>
* <ImgResSection><Image key="……" uri="……" /></ImgResSection>
* </AppRoot>
* #endregion
*/
public
void
GetBitmapImageInZip(Uri uri, Action onZipLoaded, Action onResourcesLoad)
{
_resourceService.Load(uri, (stream, status)
=>
{
if
(status
!=
DownloadStatus.Completed)
{
#if
DEBUG
throw
new
Exception(
"
error in download resource
"
);
#endif
}
onZipLoaded.ExecuteSecurity();
StreamResourceInfo info
=
new
StreamResourceInfo(stream,
null
);
using
(var reader
=
new
StreamReader(Application.GetResourceStream(info,
new
Uri(IMG_ZIP_XML, UriKind.Relative)).Stream))
{
var xmlReader
=
XmlReader.Create(reader);
while
(xmlReader.Read())
{
if
(xmlReader.IsStartElement(
"
ImgResSection
"
))
{
while
(xmlReader.Read())
{
if
(xmlReader.IsStartElement(
"
Image
"
))
{
xmlReader.MoveToAttribute(
"
key
"
);
xmlReader.ReadAttributeValue();
string
imgKey
=
xmlReader.Value;
xmlReader.MoveToAttribute(
"
uri
"
);
xmlReader.ReadAttributeValue();
var targetUri
=
new
Uri(xmlReader.Value, UriKind.RelativeOrAbsolute);
var bitmapStream
=
Application.GetResourceStream(info, targetUri).Stream;
var image
=
new
BitmapImage();
image.SetSource(bitmapStream);
_namedImageCache[imgKey]
=
image;
}
}
}
}
}
onResourcesLoad.ExecuteSecurity();
#region
zip图片资源包由GameResService托管,将WebClientResourceService中的缓存持久化
_resourceService.PutMemoryCacheToIsolatedStorage(uri);
#endregion
});
}
public
BitmapImage GetImageByUri(Uri uri)
{
if
(
!
_imageCache.ContainsKey(uri))
return
null
;
return
_imageCache[uri];
}
public
BitmapImage GetImageBySpecialName(
string
key)
{
if
(
!
_namedImageCache.ContainsKey(key))
return
null
;
return
_namedImageCache[key];
}
}
}
本文源码下载
最后祝大家圣诞快乐Merry Christmas!