silverlight游戏设计(二)资源管理篇(下)--资源的状态通知、管理与缓存
2010-12-24 19:18 姜 萌@cnblogs 阅读(1248) 评论(0) 编辑 收藏 举报在上篇中,我们实现了一个带有池化、并发连接限制能力的资源下载器,虽然这个下载器能够使我们的游戏动态加载资源并在加载完毕/失败后调用回调方法。但是还是缺乏一些智能。比如我要加载一个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);
}
{
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;
}
}
}
{
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];
}
}
}
{
/// <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!