前言
前面2篇文章分别学习了 Glide 和 Fresco ,如果说之前的 Glide 在内存上不如 Fresco 优秀的话,那么至少新版的 Glide V 4.X 版本在 API 21 之后的android 系统上和 Fresco 打成平手了,甚至比 Fresco 在 Heap 堆内存占用上还要优秀一些,当然优秀有限,这样因为 Fresco 可不会像 Glide 一样根据 view 的宽高压缩图片分辨率,当然 Fresco 我们可以在每次请求时根据 view 的宽高来计算图片的分辨率,然后 resize ,当时这种方法毕竟不如 Glide 原生实现的优秀。
图片加载库有不少,但是随着时间推移,相关项目有的稳步发展,有的趋于沉寂,综合性能,功能来说,现在给我们的选择其实只有2个 - Glide 和 Fresco:
-
Glide
新版 Glide 在 5.0 之后的系统性能比 Fresco 稍好,但是功能没有 Fresco 强,包的体积小,只有900K 左右 -
Fresco
Fresco 太过于重量了,最新版本的大小有 5M 左右,但是功能很多,5.0 之前的系统,Fresco 因为使用 Ashmeme 匿名共享内存的关系,性能那是杠杠的。
最大的困难
我们在封装自己的图片加载库功能模式时,最大的困难来源于 Fresco,对 Fresco 这货因为支持太多的功能了,所以是用的 view 是自己写的,不再是 imageview 了,是 SimpleDraweeView ,这货虽然继承自 imageview ,但是 imageview 的相关方法都被注为过时了,都不能用。控件的不统一,造成了我们 xml 书写的最大问题,到底用那个 view 啊,要是哪天 Fresco 过时了,再换一个库还来一个新的 view 怎么办啊,所以这个问题真是太坑了。
在介绍 Fresco 的章节中,提了一下 Fresco 的非侵入式设计,里面说了3种 Fresco 的兼容方式, 但是根据查阅资料和实际对比:
-
SimpleDraweeView + imageview 切换型
这种方式性能不好,控件对象对多不少,有其是在列表中,而且无法兼容动画。 -
自定义 view ,使用 Fresco 的数据层
数据层是可以拿出来的,但是 SimpleDraweeView 可是控制了 bitmap 的回收,缓存,整个视图层功能实现的,这些东西官方也是不建议自己去做的,非要自己去做的话也可以,下面有一个开源库,就是使用的 Fresco 的数据层+view层+自定义 view 实现的。 -
还是自定义 view,但是继承 SimpleDraweeView
这个方式中,这个自定义 view 里面只封装 load 功能,其实不动。
结论是我们要使用 Fresco 的化,SimpleDraweeView 还是无法抛弃的,目前最好的方式还是:方式3,我们以后切换图片加载库的时候,更新继承的父类,替换功能实现类就行,虽然还是要硬性改动代码,但是只涉及到这个 自定义 view,范围只有一处,也算是可以接受的。说真的未来新的库也是很有可能上自己的 view 的,使用原生 imageview 的话可是实现不了太多功能的。
功能封装
前面说了各个图片加载库的差异性很大,尤其期中涉及到了 view 层,不单单是功能实现层。所以我们要仔细思考如何能做的易扩展,好维护。
看了几个实现:
-
GSYImageLoader
这个实现可以做到 Glide 和 Fresco 来回切换,但是没有解决 view 的问题。优点在于功能封装的不错 -
一种使用 Fresco 非侵入式加载图片的方式
使用了 Fresco 提供的 DraweeHolder 来实现的自定义 view,跑了下 demo ,没啥问题,当然也是 demo 中都是使用的常用功能,这种方式我只找到这一个开源的demo,虽然文档中写了支持自定义 view ,但是也说了需要处理不少问题,容易给自己挖坑。所以我觉得编码能力强的同学可以研究一下,普通的开发者先不要尝试了
GSYImageLoader
这个实现可以做到 Glide 和 Fresco 来回切换,但是没有解决 view 的问题。优点在于功能封装的不错,类设计很情绪,库是用 kotlin 编写的。
- GSYLoadOption
把图片加载中 view 的各种状态,设置封装到这个 option 类中 - GSYImageLoaderManager
负责初始化,确定和切换具体的图片加载库类型 - GSYImageConst
各种状态值,静态参数 - GSYImageLoader
图片加载的功能接口,下面是实现
interface GSYImageLoader {
/**
* 加载图片
* @param loadOption 加载图片配置
* @param target 加载目标对象,ImageView or SimpleDraweeView
* @param callback 加载回调
* @param extendOption 额外配置接口
*/
fun loadImage(loadOption: GSYLoadOption, target: Any?, callback: Callback?, extendOption: ExtendedOptions? = null)
/**
* 清除缓存
* @param type GSYImageConst,清除类型
*/
fun clearCache(type: Int = GSYImageConst.CLEAR_DISK_CACHE)
/**
* 清除指定缓存
* @param type GSYImageConst,清除类型
* @param loadOption 加载图片配置
*/
fun clearCacheKey(type: Int = GSYImageConst.CLEAR_DISK_CACHE, loadOption: GSYLoadOption)
/**
* 是否已经缓存到本地
* @param loadOption 加载图片配置
* @param extendOption 额外配置接口
* @return Boolean 是否已经缓存到本地
*/
fun isCache(loadOption: GSYLoadOption, extendOption: GSYImageLoader.ExtendedOptions? = null): Boolean
/**
* 获取本地缓存
* @param loadOption 加载图片配置
* @param extendOption 额外配置接口
* @return File
*/
fun getLocalCache(loadOption: GSYLoadOption, extendOption: GSYImageLoader.ExtendedOptions? = null): File?
/**
* 获取本地缓存bitmap
* @param loadOption 加载图片配置
* @param extendOption 额外配置接口
* @return Bitmap
*/
fun getLocalCacheBitmap(loadOption: GSYLoadOption, extendOption: GSYImageLoader.ExtendedOptions? = null): Bitmap?
/**
* 获取本地缓存大小
* @return Long
*/
fun getCacheSize(): Long?
/**
* 下载图片
* @param loadOption 加载图片配置
* @param callback 加载回调
* @param extendOption 额外配置接口
* @return Bitmap
*/
fun downloadOnly(loadOption: GSYLoadOption, callback: GSYImageLoader.Callback?, extendOption: GSYImageLoader.ExtendedOptions? = null)
/**
* 额外配置支持
*/
interface ExtendedOptions {
/**
* @param option 配置对象
* Glide com.bumptech.glide.request.RequestOptions
* Picasso com.squareup.picasso.RequestCreator
* Fresco com.facebook.imagepipeline.request.ImageRequestBuilder
*/
fun onOptionsInit(option: Any?)
}
/**
* 回调接口
*/
@UiThread
interface Callback {
fun onStart()
fun onSuccess(result: Any?)
fun onFail(error: Exception?)
}
}
Fresco 代码封装看这篇,API 言简意赅,使用简单,封装优秀,思路简单