提示:下面的这种思路从一开始就很不妥,废弃 (2019.6.3),严厉批评!
对于Win8中rss新闻阅读类应用一定会面临这种问题,那就是如果断开了网络连接rss的文章该怎么呈现?
我认为既然在联网状态下我已经看到过的信息在断网之后依然能够看到。此时,该如何缓存的问题就出来了。
如果类比浏览器缓存,我们自然会问对于Win8 app获取网络数据操作系统会自动帮我们缓存一份吗?答案是肯定的。让我们看看它在哪里。
> app操作系统自身缓存
打开应用的ApplicationData文件夹,在AC文件夹下将系统所保护的隐藏文件显示出来
可以看到隐藏的INetCache、INetCookies和INetHistory,很明显是用来缓存的。通过查看每一个文件夹可以发现,我们的xml数据缓存到了INetCache文件夹下。
点击里面的每一个文件夹就可以看到xml中所包含的图片,以及xml文件。
以上是操作系统的缓存,在断网的情况下数据还是能保留的确省了App不小的心。但有一点,如果时间长了缓存的内容越来越多占用的空间磁盘比较大,或者说一次缓存就占用的空间比较多,对有些人来说这没什么。但对我个人,我觉得如果因为我的软件占用磁盘空间过多给用户带来困扰,我心里会很不好过。所以,我一定要给用户能够清理缓存的功能。
直接清理操作系统缓存的文件可以吗?答案是不可以,因为无权访问那个文件夹。
接下来的问题又来了,既然我们不能清理系统缓存的,那我们就不用系统缓存,我们自己去实现自主缓存。说做就做(前提是已经想好了)。
> xml资源自主缓存的实现
下面是思路和关键代码:
1、从网络获取XMLDocument格式的数据
可以使用XmlDoucment的LoadFromUriAsync方法来获取xml格式的数据
1 await XmlDocument.LoadFromUriAsync(new Uri("http://xxx")),"CachedFeed")
如果需要注意编码格式的话可以使用StreamReader类来读取
1 XmlDocument xml = new XmlDocument(); 2 3 StreamReader reader = new StreamReader(await client.GetStreamAsync(url), Encoding.GetEncoding("utf-8")); 4 5 string rss = await reader.ReadToEndAsync(); 6 7 xml.LoadXml(rss);
当然你也可以使用其他方法来获取指定xml的uri的byte,然后再进行转换来获取xml格式数据
1 byte[] urlContents = await client.GetByteArrayAsync(url);
(微软有示例叫做Async Sample Bridging between .NET and Windows Runtime WhenAny和Async Sample Reentrancy in Windows Store Apps (C# and Visual Basic),这两个完全可以满足异步的要求,视情况而定)
2、从XMLDocument格式的数据之后从中选出是图片的标签
1 Content.GetElementsByTagName(MediaElementTag)
3、 遍历其中每一个item的图片标签进行修改和存储
- 首先,在本app有权访问的文件夹下以OpenifExist方式创建一个文件夹
1 StorageFolder MediaFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Media", CreationCollisionOption.OpenIfExists);
- 接下来,思考怎么对缓存到本地的图片进行命名,这里最好是使用图片本来的名字。
1 CacheImage(new Uri(MediaElement.Attributes[0].InnerText), FileName);
- 然后,通过BackgroundDownloader以ReplaceIfExist的方式异步下载图片存储
1 public async static void CacheImage(Uri Source, string FileName) 2 { 3 try 4 { 5 StorageFolder MediaFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Media", CreationCollisionOption.OpenIfExists); 6 7 StorageFile destinationFile = await MediaFolder.CreateFileAsync( 8 FileName, CreationCollisionOption.ReplaceExisting); 9 10 BackgroundDownloader downloader = new BackgroundDownloader(); 11 DownloadOperation download = downloader.CreateDownload(Source, destinationFile); 12 await download.StartAsync(); 13 14 } 15 catch (Exception ex) 16 { 17 18 } 19 }
当这个图片存储完成以后,为了在断网的情况下能够访问到我们缓存的图片,这时我们要修改获取到的xml格式数据的content(注意这时我们从网络获取到的xml格式数据还没有存储到本地)
4、储存我们已经修改好的xml数据
1 var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(Name, CreationCollisionOption.ReplaceExisting); 2 var data = Content.GetXml(); 3 await FileIO.WriteTextAsync(file, data);
(之前有朋友问到为什么以ReplaceExisting的方式在app的安装文件夹(不是本示例中的ApplocationData文件夹)创建文件提示“拒绝访问”,因为app不像普通的桌面软件咱们能够本地随意修改,微软也是为了防止app被修改以保证app的质量,更多解释可以参考这篇MSDN帖子http://social.msdn.microsoft.com/Forums/zh-CN/d8e42152-0b7b-4216-8dc9-18ae1ad152cf/storagefile#ca74e62d-d2e1-4e2f-ab8f-1f0d395fb591)
5、既然xml本地缓存好了读取就很简单了,但要注意以下两个条件:
- 我们要确定好多长时间更新一下缓存,超出这个时间再去访问网络更新资源
- 当我们要访问网络,但发现网络不可用的时候就要去访问本地存储的资源了,下面的代码来判断网络是否连接
1 public static bool CheckConnection() 2 { 3 try 4 { 5 string connectionProfileInfo = string.Empty; 6 ConnectionProfile InternetConnectionProfile = NetworkInformation.GetInternetConnectionProfile(); 7 if (InternetConnectionProfile == null) 8 { 9 return false; 10 } 11 var networkConnectivityLevel = InternetConnectionProfile.GetNetworkConnectivityLevel(); 12 13 14 if ( 15 networkConnectivityLevel == NetworkConnectivityLevel.LocalAccess 16 || networkConnectivityLevel == NetworkConnectivityLevel.None) 17 { 18 return false; 19 } 20 else 21 { 22 23 return true; 24 } 25 26 } 27 catch (Exception ex) 28 { 29 return false; 30 } 31 32 }
更多详细请参见源码
(源代码链接不再提供)
问:假如xml的图片正在下载,未下载完成,此时我就去读不就出错了吗?
答:这个问题已经考虑到,如果你的网速足够快可以不考虑这种情况。但我非要考虑这种情况,那么你可以使用SemaphoreSlim,可对同时访问资源或资源池的线程数加以限制。
问:我们知道只有在控件需要显示图片的时候图片才会被拉取,图片才会被缓存,那采用你这个方法岂不是不管控件需不需要显示图片都要去下载图片吗?
答:的确存在这种问题,这种方法最好是用在所有控件都是可见显性的情况下吧。更好的解决方法大家多多讨论。
问:我是先将获取修改好的xml数据绑定到控件好还是先把xml存到本地再去读一次xml好呢?
答:视情况而定吧,我觉得直接将修改好的xml数据绑定到控件比较好。
上面只是示例了对图片的缓存,当然也能缓存视频,更多问题还请大家多多发现,相互学习,共同提高。