图片的加载和显示是每个商业APP都避免不了的问题,对于图片重度依赖类APP,例如壁纸类应用,图片社交类应用,对于图片的处理将会影响到整个APP的用户体验。
在正式了解Android中如何优化图片相关的内容之前,我们先看看Android系统支持的图片格式。
一、图片的格式
目前移动端Android平台原生支持的图片格式主要有:JPEG、PNG、GIF、BMP和WebP(自Android4.0开始支持),但是在Android应用开发中能够使用的编解码格式只有其中三种:JPEG、PNG、WebP,图片格式可以通过查看Bitmap类的CompressFormat枚举值来确定。
public enum CompressFormat {
JPEG (0),
PNG (1),
WEBP (2);
CompressFormat(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
如果要在应用层使用GIF格式图片,那么需要自己引入第三方函数库进行支持。
(1)、JPEG
JPEG是一种广泛使用的有损压缩图像标准格式,它不支持透明和多帧动画,一般摄影类作品最终都是以JPEG格式展示,通过控制压缩比,可以调整图片的大小。
(2)、PNG
PNG是一种无损压缩图片格式,它支持完整的透明通道,从图像处理领域讲,JPEG只有RGB三个通道,而PNG有ARGB四个通道。由于是无损压缩,因此PNG图片占用空间一般比较大,会无形中增加最终APP的大小,在做瘦身时一般都要对PNG图片进行处理以减小其占用的体积。
(3)、GIF
GIF是一种古老的图片格式,它诞生于1987年,随着初代互联网流行开来。它的特点是支持多帧动画。大家对这种格式肯定不陌生,社交平台上面发送的各种动态表情,大部分都是基于GIF来实现的。
(4)、WebP
相对于前面几种图片格式,WebP算是一个初生儿,由Google在2010年发布,它支持有损和无损压缩、支持完整的透明通道、也支持多帧动画,是一种比较理想的图片格式。目前国内很多主流APP都已经应用了WebP,例如微信、微博、淘宝等。在即保证图片质量又要限制图片大小的需求下,WebP应该是首选。
二、图片的压缩
目前无论Android平台还是IOS平台,大多数APP在搭建界面时使用的几乎都是PNG格式图片资源,除非你的项目已经全面支持WebP格式,否则你都会面临对PNG图片瘦身的要求,在这里,我们可以通过几个工具对PNG图片进行压缩来达到瘦身的目的。
(1)、无损压缩ImageOptim(https://imageoptim.com)
ImageOptim是一个无损的压缩工具,它通过优化PNG压缩参数,移除冗余元数据以及非必需的颜色配置文件等方式,在不牺牲图片质量的前提下,即减小了PNG图片占用的空间又提高了加载的速度。
(2)、有损压缩ImageAlpha(https://pngmini.com/)
ImageAlpha是ImageOptim作者开发的一个有损的PNG压缩工具,相比较而言,图片大小得到极大的降低,当然图片的质量同时也会受到一定程度的影响,经过该工具压缩的图片,需要经过设计师的检查才能最终上线,否则可能会影响到整个APP的视觉效果。
(3)、有损压缩TinyPNG(https://tinypng.com/)
TinyPNG也是比较知名的有损PNG压缩工具,它以Web站点的形式提供,没有独立的APP安装包,同所有的有损压缩工具一样,经过压缩的图片,需要经过设计师的检查才能最终上线,否则可能会影响到整个APP的视觉效果。
其实还有很多无损压缩工具,例如JPEGMini(http://www.jpegmini.com/),MozJPEG(https://imageoptim.com/mozjpeg)等,大家都可以从中选择适合自己项目的一个就行,主要是在图片大小和图片质量之间找到一个折衷点。
(4)、PNG/JPEG转换为WebP
如果你的APP最低支持到Android4.0,那么可以直接使用系统提供的能力来支持WebP,如果是4.0以下的系统,也可以通过在APP中集成第三方函数库例如webp-android-backprot(https://github.com/alexey-pelykh/webp-android-backport/tree/master/webp-android-backport-library)来实现WebP的支持。根据Google的测试,无损压缩后的WebP比PNG文件少了45%的文件大小,即使这些PNG文件经过其他压缩工具例如ImageOptim(https://imageoptim.com)压缩之后,WebP依然可以减少约28%的文件大小。
WebP转换的工具可以选择智图(http://zhitu.isux.us/)和iSparta(http://isparta.github.io/)等;
(5)、尽量使用NinePatch格式的PNG图
.9.png图片格式简称NinePatch图,本质上仍然是PNG格式图片,它是针对Android平台的一种特殊PNG图片格式,可以在图片指定位置拉伸或者填充内容。NinePatch图的优点是体积小,拉伸不变形,能够很好的适配Android各种机型。Android SDK自带了NinePatch图的编辑工具,位于sdk/tools/draw9patch,点击即可启动;当然,Android Studio也集成了PNG转NinePatch的功能,我们只需要右键点击某个需要转换的PNG图片,在弹出的对话框中选择Create 9-Patch File...即可自动完成转换。
三、图片的缓存
Android发展到今天,已经出现很多优秀的图片缓存函数库,开发人员可以根据项目的实际需求进行选择,传统的图片缓存方案中设置有两级缓存,分别是内存缓存和磁盘缓存。在Facebook推出的Fresco中,它增加了一级缓存,也就是Native缓存,这极大地降低了使用Fresco的APP出现OOM的概率。
(1)、BitmapFun
BitmapFun函数库是Android官方教程中的一个图片加载和缓存示例,对于简单的图片加载需求来说,使用BitmapFun就够了,在早期的Android APP开发中使用较多,后来随着越来越多成熟强大的函数库的出现,BitmapFun也渐渐退出实际项目开发的舞台。但作为一个入门图片缓存的教程,它依然起着不可忽视的作用,毕竟万变不离其宗。
(2)、Picasso(https://github.com/square/picasso)
Picasso是著名的Square公司众多开源项目中的一个,以著名画家毕加索为名,连Sample app都使用毕加索的名画作为例子。它除了实现图片的下载和二级缓存功能,还解决了常见的一些问题。
- 在adapter中正常的处理Image View 回收和下载的取消。
- 使用尽量小的内存实现复杂的图片变换
在Picasso中,我们使用一行代码即可实现图片下载并渲染到ImageView中。
Picasso.with(context).load("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png").into(imageView);
(3)、Glide(https://github.com/bumptech/glide)
Glide是Google推荐的用于Android平台上的图片加载和缓存函数库。这个库被广泛应用在Google的开源项目中,Glide和Picasso有90%的相似度,可以说就是Picasso的克隆版本,只是在细节上还是存在不少区别。Glide为包含图片的滚动列表做了尽可能流畅的优化。除了静态图片,Glide也支持GIF格式图片的显示。Glide提供了灵活的API可以让开发者方便地替换下载图片所用的网络函数库,默认情况下,它使用HttpUrlConnection作为网络请求模块,开发者也可以根据自己项目的实际需求灵活使用Google的Volley或者Square的OkHttp等函数库进行替换。
Glide的使用也可以使用一行代码来完成,语句如下,跟Picasso确实非常相似。
Glide.with(this).load("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png").into(imageView);
(4)、Fresco(https://github.com/facebook/fresco)
Fresco是Facebook开源的功能强大的图片加载和缓存函数库,相比其他图片缓存库,Fresco最显著的特点是具有三级缓存:两级内存缓存和一级磁盘缓存。它的主要特性如下。
- 渐进式地加载JPEG图片。
- 显示GIF和WebP动画。
- 可扩展,可自定义的图片加载和显示。
- 在Android4.x和以下的系统上,将图片放在Android内存一个特殊的区域,从而使得应用运行更流畅,同时极大减低出现OutOfMemoryError错误。
- Facebook给出了中文官方API:Fresco中文官方API
(5)、Android-Universal-Image-Loader(https://github.com/nostra13/Android-Universal-Image-Loader)
Android-Universal-Image-Loader简称UIL,是Android平台老牌的图片下载和缓存函数库,功能强大灵活且高度可自定义,它提供一系列配置选项,并能很好地控制图片加载和缓存的过程。使用者甚多,早期的Android开发者应该都接触过,现在仍然在很多项目中使用。UIL也支持二级缓存,它的主要特性如下。
- 同步或者异步的多线程图片加载。
- 高度可自定义:线程池、下载器、解码器、内存和磁盘缓存、图片显示选项等。
- 每张图片的显示支持多种自定义选项:默认存根图片、缓存切换、解码选项、Bitmap处理和显示等。
- 图片可缓存在内存或者磁盘(设备的文件系统或者SD卡)上;
- 可实时监听图片加载流程,包括下载进度。
最后来看一下如果引入这些函数库,会给APP增加多大的空间:
可以看到,Fresco函数库的依赖库有很多,除了他几个函数库的jar以外,其他的都是使用Fresco所需要依赖的。总结起来,以上函数库大小如下:
- Picasso:118kb
- Glide:465kb
- Fresco:770kb
- Android-Universal-Image-Loader:159kb
图片函数库的选择需要根据APP的具体情况而定,对于严重依赖图片缓存的APP,例如壁纸,图片社交类APP来说,可以选择最专业的Fresco。对于一般的APP,选择Fresco会显得比较重,毕竟Fresco 770kb的体量摆在这儿。根据APP对图片显示和缓存的需求从低到高我们可以对以上函数库做一个排序。
Picasso<Android-Universal-Image-Loader<Glide<Fresco
值得一提的是,如果你的APP计划使用React Native 进行部分模块功能的开发的话,那么在基础函数库选择方面需要考虑和React Native的依赖库的复用,这样可以减少引入React Native 所增加的APP大小,可以复用的函数库有:OkHttp、Fresco、jackson-core。
四、开源代码库
最后再分享一个自己积攒很久的代码库,只有你想不到,没有用不到的,欢迎star
由于github服务器在美国,有时访问很慢,还提供了开源中国地址库,2个仓库代码均同步更新: