图片加载是Android开发中基础的功能,同时图片加载OOM也一直困扰着很多开发者,
因此为了降低开发周期和难度,我们经常会选用一些图片加载的开源库。
老牌的有ImageLoader,UIL,Volley,主流的有,Picasso,
Glide,Fresco等等,选择一款好的图片加载裤就成了我们的首要问题。
接下来我们对比一下主流的两款 Glide,Fresco框架的优缺点
简介
首先来简单的介绍一下这俩个图片加载框架。
Glide
Glide是Google的一位大佬的杰作,
基于Picasso,沿袭了Picasso的简洁风格,并且在此做了大量的优化与改进。Glide
默认的Bitmap格式是RGB_565,所以占用的内存比较小。在磁盘缓存时,Glide支持
缓存多种尺寸,这样Glide在加载速度上也具有一定的优势,可以根据View的大小去加载
图片不用加载全尺寸图片。
除此之外,Glide还支持加载Gif动态图,支持很多自定义样式,比如圆角等等。
Fresco
Fresco是Facebook出品,他是新一
代的图片加载库。我们知道,Android的内存资源是十分有限的,加载图片的时候经常会
出现OOM的情况,虽然可以采用各种方法去优化内存的使用,但是却无法再本质上解决内存
资源稀缺的问题。OOM问题在低端机上问题尤为严重,而Facebook另辟蹊径,在更加底层
的Native层做处理,不去占用Java虚拟机层宝贵的内存资源。Fresco将图片当道一个叫
Ashmem的区域,这样由于图片不占用Java虚拟机的资源,所以大大减少了OOM。
虽然此库很强大,不用用起来很麻烦,包也很大,有2M大小,由于涉及Native层,想读源
码比较困难。
Glide与Fresco详细对比
基本信息对比
对比项 | Glide | Fresco |
---|---|---|
发布时间 | 2014.9 | 2015.5 |
是否支持webP | true | true |
大小 | 500K | 2M~3M |
是否支持Gif | true | true |
视频加载 | true | true |
开发者 |
功能对比
Fresco功能列表
- 加载进度提示
- 圆角图片
- 渐进式加载
- 显示动图GIF
- 多图请求
- 监听下载
- 缩放 旋转
可以看出,Fresco不仅可以实现一些基本的图片加载图片缓存,而且还提供一些变化功能,比如圆角等,这为我们的开发提供了极大的方便,提高开发效率。
当然还有一个问题,那就是实现的便捷性怎么样,是不是能够让我们很轻松的切换过来。对于Fresco来说,这一点稍微有一点麻烦,如果我们相拥Fresco加载图片,就不能使用原生的ImageView,而是必须使用Drawees这个View,所以说,我们可能需要全局替换一下ImageView,自定义的ImageView的父类也需要修改一下,这种修改就可能会给我们带来一些未知的风险。
撇下这个不谈,我们看一下具体功能的实现方式。
圆角的实现
1 | public void setRoundImageSrc(SimpleDraweeView draweeView, String src, float radius){ |
通过上面代码就可以实现圆角的功能了,代码还是很简单的,简单的添加一些参数即可。
缓存处理
Fresco的缓存也是一大亮点,它采用了三级缓存的方式,分别是Bitmap缓存,未解码图片缓存,图片文件缓存。
注意:在Android 5.0以下,bitmap缓存位于ashmem区域,而在Android 5.0以上,bitmap和其他框架一样,缓存在Java Heap之上。因为Android 5.0系统之上,内存管理有了很大改进,OOM的情况减少了不少。
1 | public void initFresco(Context context, String diskCacheUniqueName){ |
通过上面代码,可以根据应用设置不同的缓存策略。
图片渐进式显示
渐进式图片格式先呈现大致的图片轮廓,然后随着图片的下载的继续,呈现捉奸清晰的图片,这对于移动设备。尤其是慢网络下的体验是极大的提升。
Android本省的图片库是不支持此格式的,但是Fresco支持。使用的时候只要制定一个URI即可,剩下的部分Fresco会帮我们处理好。
示例代码如下:
1 | // 第一步 初始化配置信息 |
监听下载事件
我们常常需要在图片加载完成后执行某些动作,比如使个别View可见,或者显示一些文字。或者是在下载图片失败后做一些事情,比如向用户显示一条失败信息,或者其他一下提示。图片加载都是在后台进程异步加载的,在Fresco中,我们需要使用DraweeController来监听下载事件。
使用方法:
简单的定义一个ControllerListener即可,官方推荐我们继承BaseControllerListener。
1 | ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() { |
我们发现,我们不仅可以通过监听获取到我们下载的进度,还可以看到尺寸信息等,这一点对我们也是很有帮助的。
Glide功能列表
- 图片样式自定义,不仅仅支持圆角图片
- 显示动图GIF
- 加载视频(不能直接加载网络资源)
- 缩放 旋转
- 支持加载的生命周期绑定
- 支持加载各种资源,如字节数组,uri资源,resource资源等等
图片变换
Glide的圆角处理相比Fresco要稍微麻烦一丢丢,但是必须注意的是,Glide为我们提供了一个非常灵活的接口去处理bitmap,所以我们不仅仅局限于实现圆角,你还可以实现各种各样的样式。
要在Glide中实现圆角的功能,需要我们先自定义一个BitmapTransformation:
1 | class RoundTransformation extends BitmapTransformation{ |
在transform这个我们中,bitmap直接交给我们处理,所以这里就有很多很多的可能性了,比如灰度,黑白图都是可以实现的,这个实现方案必须点赞打call。
缓存
Glide为我们提供了很完善的缓存管理方法。前面我们提到过,Glide可以根据View大小自动决定加载图片的大小。比如一张10801920大小的图片,我们仅仅把他显示在了108192大小的View上面,那么Glide会很只能的将图片尺寸变小成和View一样大小的尺度,减少内存的使用。显示在View上面的图片同样是108192的,比原始尺寸整整小了100倍。对于缓存来说,Glide会将这个108192大小的图片缓存到磁盘中,要是我们下一次将他显示在了216*364大小的View上面的话,那么就必须重新从网络上获取一张图了。当然Glide为我们提供了设置接口:
1 | Glide.with(this).load(imageUrl).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView); |
- DiskCacheStrategy.ALL 缓存源资源和转换后的资源
- DiskCacheStrategy.NONE 不作任何磁盘缓存
- DiskCacheStrategy.SOURCE 缓存源资源
- DiskCacheStrategy.RESULT 缓存转换后的资源
如果我们不想使用缓存,每次都要从网络上面加载图片资源的话可以这样:
1 | Glide.with(this).load(imageUrl).skipMemoryCache(true).into(imageView); |
清理图片缓存:
1 | // 清理缓存 |
ps:需要注意的是,清理磁盘缓存一定要在子线程中调用,清理内存的时候可以在UI线程。
其他的缓存设置还可以通过自定义GlideModule来实现:
1 | public class MyGlideModule implements GlideModule { |
通过builder,我们可以设置内存缓存的大小等内容。可以见得,Glide的定制化做的很棒,提供了各种自定义功能。
监听下载
Glide提供了RequestListener<T, T>来监听下载,不过他只有加载成功和加载失败的回调,没办法获取当前下载的进度。
1 | public abstract class LoadCallback { |
不过,还是有其他方法来实现这一功能的,Glide的图片下载是基于OKHTTP实现的,所以我们可以通过拦截器获取到当前的下下载进度。
我们可以创建一个ProgressModelLoader类,实现StreamModelLoader接口:
1 | public class ProgressModelLoader implements StreamModelLoader<String> { |
重写getResourceFetcher方法,这个方法返回一个DataFetcher类,这个类是个数据提取类,是个接口,重写他的loadData方法来下载图片,我们老看下ProgressDataFetcher对loadData方法的实现:
1 | @Override |
为OKHTTP添加一个拦截器:
1 | public class ProgressInterceptor implements Interceptor { |
重写intercept方法,创建一个ProgressResponseBody得到图片下载进度:
1 | private Source source(Source source) { |
把读到的bytesRead和responseBody.contentLength()传给回调方法
progressListener.progress来计算进度。这样就可以实现下载进度的监听了。
Glide与Fresco的使用小结
相比于Glide,Fresco可以说是更专业的一款图片加载框架,它在内存管理方面有着不可比拟的优势,加载速度也很不错,特别是对Android 5.0的内存优化方面,大大减少了OOM的情况发生。但是另一个方面,它的包有2M之大,而且对于一般的App而言,使用Fresco有点大材小用的意思,Glide已经满足我们绝大部分需求了,除非你是Instagram之类的图片社交应用,否则的话Glide就已经足够使用了。Fresco更加专业,使用起来也更加有技巧,需要开发认真研究一下官方文档才能用的更好,而Glide则是为大部分应用而生,上手简单,满足大部分需求,足以。
思考:哪一天Glide被淘汰了我们怎么办?
UniversalImageLoader可以算是老牌的图片加载库,在GitHub上面有13000+
的star,火爆程度令人发指。但是。很遗憾的是,这个项目已经不在维护了。这就意味着以后任何bug都不会修复,新特性也不能开发了,所以继续使用就可能会遇到很多麻烦了。同样的,万一哪一天Glide也不维护了呢?或者说我们应用有新的需求,需求更换新的图片加载库,我们怎么办呢?总不能一处一处的替换吧。
所以呢,想了一下,我们可以自己动手编写一个代理,让代理帮我们下载图片,某一天,我们想换图片加载框架了,只需要换掉代理即可,我们实现的那些下载进度监听,下载失败监听等等代码都不要更改,仅仅是将代码修改一下即可,这样也方便我们调试代码,岂不快哉!
这样,我们程序调用就成了这样:
实现步骤
每一个图片加载可能都需要一个特殊的设置吗,比如圆角,下载回调等等,所以我们最后先同一一个下载的Config:
1 | public class ImageLoadConfig { |
注意:这些属性都不要和具体的某一个加载框架耦合在一起,这些信息都是我们自定义的
接着我们先定一个图片加载的接口,他接受config作为参数:
1 | public interface IImageLoad { |
紧接着,我们使用框架实现这个加载过程:
1 | public class GlideImageLoad implements IImageLoad { |
这个config里面的属性配置可以比较灵活,你们需要什么样的样式定制随时可以添加定制,而代码的改动仅仅是config和某一个具体的下载类而已,很方便,有一种予取予求的感觉~
最后,我们定义一个代码类,通过他连接具体的程序调用和框架下载程序:
1 | public class ImageLoadProxy { |
使用示例:
1 | ImageLoadProxy.getInstance().load(new ImageLoadConfig() |
这样,我们的图片加载就完全和具体的加载框架解耦了,改换其他的方案也是分分钟就能搞定的了。
总结
Fresco和Glide各有各优势,Fresco更加专业,Glide更加灵活小巧,不同项目可以选择不同的框架就可以了。另外,更重要的一点就是要未雨绸缪,万一哪一天需要更改了,想想那可怕的工作量,所以还是早早的上代理的好呢~