在开发WP7程序过程中,会遇到在UI上使用用大量的Image这种情况。你可能会以为使用Image是一个很简单的事情,不需要用一篇博客的篇幅的介 绍:仅仅设置一个Uri给Image的Source属性就完成了?但是,还有其他的事情需要考虑呢。这里有许多的小细节如果你知道到的话,会对的你的程序 有帮助,特别是当希望开发出的软件能够有较好的体验和较少的内存使用(移动开发这点很重要)。这些小的提示,不仅适用WP7手机开发,同样也适用 SilverLight桌面程序。但是在手机开发中,把握住那些能够把一个好的程序变成一个优秀的程序性能细节是非常重要的技能。为了指出这一点,这里举 出一个实现了这些小细节的示例。
JPG VS PNG
如果要在JPG和PNG之间做个选择,那为你的所有不透明的图片都使用JPG吧。Window Phone 7操作系统上的JPG解码器相比之下要比PNG解码器快很多。当然,如果你的图片需要是透明的,那PNG是你唯一的选择了。
Resource vs Content
在Visual Studio中把“Build Type”设置成“Resource”或者“Content”,图片都会编译成Window Phone XAP文件的一部分。由于他们都被打包到XAP中,起初可能你看不出他俩有什么区别,貌似都是一路货色。但当你撬开XAP的外壳(重命名为.zip文件并 解压缩):那是你可以看到所有的“Content”类型文件,却找不着“Resource”类型文件。为什么?因为“Resource”类型的文件嵌到了 程序集里了(也就是dll文件里)。那么,我们为何又如此关心“Resource”文件到底是嵌到哪儿去了。这是因为你的程序的DLL包越大,你的程序启 动就会越慢。但另一方面,跟图片从磁盘加载相比,嵌入DLL的图片加载会更快速。
Summary:设置成”Content“会让程序启动更快,设置成”Resource“会让程序更快的响应。
还有值得一提的就是他们有着不同的URI格式,可以参考下面的例子:
Content: <Image Source="/ImagesAsContent/smiley1.png"/>
Resource: <Image Source="..\ImagesAsResource\smiley3.png"/>
Async vs.Sync Loading of images
如果你专研过Imaging API 你会发现BitmapImage这个类,在创建一个Image的时候BitmapImage会在幕后做很多的工作。它能以两种模式加载内容:
BitmapImage.UriSource = uriSource; // loads the image via URI, asynchronously
BitmapImage.SetSource(stream); // loads the image from stream, synchronously
注意到异步加载图片并不意味着完全的off-thread。这是因为有关图片的解码是发生在UI线程上的。关于在保持UI线程响应的情况下,异步加载图片的详细信息参考David Anson的博客.
下面是一些关于同步vs异步加载的知识:
1.如果同步加载一张不正确的图片文件,将会得到一个异常。
2.如果异步加载一张不正确的图片文件,ImageFailed事件会触发。
3.如果异步加载一张正确的图片文件,ImageOpened事件会触发。
4.如果同步加载一张正确的图片文件,ImageOpened事件不会触发。
它们的不同可以通过我给出的示例代码演示(在Loading页面里)
Image Caching
这是图片比较重要的一个方面,MSDN对于它的记录也很少。在WP7 Platform中即使清空了Source属性并且把Image从visual tree中去除,Image的内存还是不会释放,其实这时你看到的是图片的缓存。这是一个预留的性能优化,是为了避免一遍又一遍的加载和解码相同的图片。 WP7中在内存中开辟了一个缓冲区,我们可以利用它方便快捷的再利用图片资源。这里与浏览器的缓存类似。
虽然 图片缓存对于提高性能有很大的帮助,但是有时候也会增加不必要的内存消耗。特别是还回收再利用那些不会再显示的图片。在程序的运行过程中,图片缓存会一直占用内存空间。不过也可以通过下列代码删除缓存信息:
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
合理的利用上述代码可以减少内存使用。而减少内存使用在手机开发中对于性能有较大的帮助。在示例程序里,在“Caching”页面中监视了显示和清除缓存的内存使用情况,在示例中可以看到有3MB的内存差异。
BitmapCreateOptions
这 是 一个快速而且很有意思的部分。创建一个BitmapImage的时候,你有可能会有这样的疑问:为什么当你认为已经完成图片创建时仍不会返回图片的大小 呢?这是因为CreateOptions 属性默认被设置成了“DelayCreation”,也就是说当BitmapImage被设置成一个Image的Source属性上或者是可视化树的 ImageBrush上,这个对象只有在真正需要它的时候才会被创建。这里就允许在程序开始运行的时候Creating很多的BitmapImage,却 不消耗任何的资源(CPU或者是内存)直到程序需要特定的图片才会真正创建它们。如果你想强制的立即执行创建操作,把CreateOptions设置成 “None”即可。CreateOptions 还有一个值是“IgnoreImageCache”,这个值在很多的场景中都不是很有用,而且他还需要和 设计工具结合(如Blend)。
在示例程序里,在“CreateOptions”页面中,通过Create/Show/Clear控制CreateOptions的值为DelayCreation,None并监视图片大小的变化。
Custom Decoding
在 有关图像的API中都是以图片自身的分辨率解码(除了Downsampling缩减像素采样,等会儿会介绍到它)。不管怎样,这已经能满足在手机上的图片 显示了。如果下载一批320X320的图片,但只以32X32那么小显示,那么还以原分辨率解码就会浪费很对的内存资源。
在 Windows Phone平台上提供给程序员们PictureDecoder的应用程序接口,能够自定义分辨率去解码图片:
image.Source = PictureDecoder.DecodeJpeg(jpgStream, 192, 256);
Note:在当前发布的API中有一个bug,图片的高度和宽度属性值被交换了。因此,得把宽度的值设定到高度上,高度同理。这个问题以后MS肯定会修复,我也是无意间发现的这个问题。
为了查看内存增长情况,在示例程序的“Custom Decoding”页面中。在两种的分辨率中监视内存使用情况。
Auto Downsampling
这是最后一个在处理WP7平台图片我们需要知道的事。background是一个特殊的元素,被限定到2048X2048。超过此大小的会被剪裁,这就意味着使用一个长宽超过2048像素的的图片,该图片不会完全显示。为了避免这种情况的发生,WP7平台会自动的降低像素是图片去适应2048X2048。这一点不同于桌面Silverlight应用,应为silverlight不存在上述限制。
在示例程序中我使用了一张3000X2102分辨率的图片,最终图片被像素缩减为1500X1051。
事实上,关于图片还有很多值得细细研究的,这里只是起一个抛砖引玉的作用,希望能和大家一起探讨这个问题。谢谢你能阅读这篇文章,希望能对你有所帮助。
能力有限,如果有错误的地方请指正。