代码改变世界

silverlight游戏设计(二)资源管理篇(下)--资源的状态通知、管理与缓存

2010-12-24 19:18 姜 萌@cnblogs 阅读(...) 评论(...) 编辑 收藏

在上篇中,我们实现了一个带有池化、并发连接限制能力的资源下载器,虽然这个下载器能够使我们的游戏动态加载资源并在加载完毕/失败后调用回调方法。但是还是缺乏一些智能。比如我要加载一个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。

image

image

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!