Windows Phone App基于文件系统的图片缓存方案
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Windows Phone App基于文件系统的图片缓存方案,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6135字,纯文字阅读大概需要9分钟。
内容图文
![Windows Phone App基于文件系统的图片缓存方案](/upload/InfoBanner/zyjiaocheng/1189/c80ad65fb14c49b8b72e699b182d6659.jpg)
最近在做一个Windows Phone 8.1的应用,因为应用里面使用了很多图片,所以打算将图片文件缓存到文件系统里面,以便节省流量和提升体验。
一般显示图片的做法大多只需要将对应的Uri地址绑定到对应控件的ImageSource属性上即可,或者将Uri传入BitmapImage对象,其会自动将资源异步下载好然后加载。
为了不将缓存的逻辑侵入实体模型层,所以打算在Uri->BitmapImage绑定上做文章,也就是利用IValueConverter接口构造值转换器。
这里有一个问题就是,因为绑定的原因,IValueConverter接口只能是同步的方法,而图片的下载的过程是一个异步的过程,所以我们需要一种解决方案实现一个异步的ValueConverter。
这个解决方案我思考了很久,最后在这里受到了启发:http://stackoverflow.com/questions/15003827/async-implementation-of-ivalueconverter。
具体而言,就是在同步的ValueConverter中构造一个Task-like的对象,然后将对应的属性绑定到这个Task-like对象的一个属性中去,然后Task-like待Task完成后更新对应的状态。
这个解决方案实现很巧妙,同时也没有对Model和ViewModel做任何的侵入,下面是我修改后用于图片缓存的相关代码:
首先是界面绑定所做的改动,大概如下,就是通过构造一个新的DataContext来实现绑定:
< Image DataContext =" {Binding Image, Converter={StaticResource ImageConverter}} " Stretch ="UniformToFill" Source =" {Binding Result} " />
对应的ValueConverter类:
public class CacheImageValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { var image = value as Image; if (image == null || string.IsNullOrEmpty(image.Url)) { var bitmap = new BitmapImage(new Uri("ms-appx:///assets/default.png")); var notifier = new TaskCompletionNotifier<BitmapImage>(); notifier.SetTask(Task.FromResult(bitmap)); return notifier; } else { var task = Task.Run(async () => { var cache = HiwedoContainer.Current.Resolve<ImageCache>(); var uri = await cache.GetImageSourceFromUrlAsync(image.Url); return uri; }); var notifier = new TaskCompletionNotifier<BitmapImage>(); notifier.SetTask(task, c => new BitmapImage(c)); return notifier; } } publicobject ConvertBack(object value, Type targetType, object parameter, string language) { thrownew NotImplementedException(); } }
其次是Task-like的类。
public sealed class TaskCompletionNotifier<TResult> : INotifyPropertyChanged { private TResult _result; private IAsyncResult _task; public TaskCompletionNotifier() { this._result = default(TResult); } publicvoid SetTask<T>(Task<T> task, Func<T, TResult> factoryFunc) { this._task = task; if (!task.IsCompleted) { var scheduler = (SynchronizationContext.Current == null) ? TaskScheduler.Current : TaskScheduler.FromCurrentSynchronizationContext(); task.ContinueWith(t => { var propertyChanged = PropertyChanged; if (propertyChanged != null) { this.OnPropertyChanged("IsCompleted"); if (t.IsFaulted) { InnerException = t.Exception; this.OnPropertyChanged("ErrorMessage"); } else { try { this._result = factoryFunc(task.Result); } catch (Exception ex) { Debug.WriteLine("Factory error: " + ex.Message); this.InnerException = ex; this.OnPropertyChanged("ErrorMessage"); } this.OnPropertyChanged("Result"); } } }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler); } else { this._result = factoryFunc(task.Result); } } publicvoid SetTask(Task<TResult> task) { this._task = task; if (!task.IsCompleted) { var scheduler = (SynchronizationContext.Current == null) ? TaskScheduler.Current : TaskScheduler.FromCurrentSynchronizationContext(); task.ContinueWith(t => { var propertyChanged = PropertyChanged; if (propertyChanged != null) { this.OnPropertyChanged("IsCompleted"); if (t.IsFaulted) { InnerException = t.Exception; this.OnPropertyChanged("ErrorMessage"); } else { this._result = task.Result; this.OnPropertyChanged("Result"); } } }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler); } else { this._result = task.Result; } } public TResult Result { get { returnthis._result; } } publicbool IsCompleted { get { return _task.IsCompleted; } } public Exception InnerException { get; set; } publicstring ErrorMessage { get { return (InnerException == null) ? null : InnerException.Message; } } publicevent PropertyChangedEventHandler PropertyChanged; [NotifyPropertyChangedInvocator] privatevoid OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }
提供一个Task和Factory的函数重载是必须的,因为对于像BitmapImage这样的类,其初始化需要在对应的UI线程中,不能直接将BitmapImage作为结果返回,所以在下面也可以看到,实际上Task返回的只是Uri,然后构造BitmapImage是在ContinueWith的方法中完成的。
最后就是用于图片缓存的类,这个类比较简单,就是如果判断如果图片已经存在,就直接从文件系统返回,如果不存在就先下载再返回对应的Uri。然后会有一个变量缓存所有已经缓存的文件名。
public class ImageCache { private const string ImageCacheFolder = "ImageCaches"; private StorageFolder _cacheFolder; private IList<string> _cachedFileNames; publicasync Task<Uri> GetImageSourceFromUrlAsync(string url) { string fileName = url.Substring(url.LastIndexOf(‘/‘) + 1); if (this._cachedFileNames.Contains(fileName)) { returnnew Uri("ms-appdata:///local/ImageCaches/" + fileName); } if (await DownloadAndSaveAsync(url, fileName)) { _cachedFileNames.Add(fileName); returnnew Uri("ms-appdata:///local/ImageCaches/" + fileName); } Debug.WriteLine("Download image failed. " + url); returnnew Uri(url); } privateasync Task<bool> DownloadAndSaveAsync(string url, string filename) { try { var request = WebRequest.CreateHttp(url); request.Method = "GET"; using (var response = await request.GetResponseAsync()) { using (var responseStream = response.GetResponseStream()) { var file = awaitthis._cacheFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting); using (var fs = await file.OpenStreamForWriteAsync()) { await responseStream.CopyToAsync(fs); Debug.WriteLine("Downloaded: " + url); returntrue; } } } } catch (Exception ex) { Debug.WriteLine("Error: " + ex.Message); returnfalse; } } publicasync Task LoadCache() { var folders = await ApplicationData.Current.LocalFolder.GetFoldersAsync(); foreach (var folder in folders) { if (folder.Name == ImageCacheFolder) { this._cacheFolder = folder; break; } } if (this._cacheFolder == null) { this._cacheFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(ImageCacheFolder); } this._cachedFileNames = (awaitthis._cacheFolder.GetFilesAsync()).Select(c => c.Name).ToList(); } public IList<string> CachedFileNames { get { return _cachedFileNames; } set { _cachedFileNames = value; } } }
原文:http://www.cnblogs.com/harrywong/p/4230443.html
内容总结
以上是互联网集市为您收集整理的Windows Phone App基于文件系统的图片缓存方案全部内容,希望文章能够帮你解决Windows Phone App基于文件系统的图片缓存方案所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。