一、图片
1、图片库对比
2、LRUCache原理
3、图片加载原理
4、自己去实现图片库,怎么做?
5、Glide源码解析
6、Glide使用什么缓存?
7、Glide内存缓存如何控制大小?
参考答案:
1、图片库对比
Picasso Glide Fresco
Picasso 毕加索 Square
Picasso 没有实现本地缓存功能,交给了 Square 的另外一个网络库 okhttp 去实现,
这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。
- 使用简单,代码简洁
- 与Square其他类库搭配兼容性好,Retrofit OkHttp 等
缺点:
- 功能简单 图片加载
- 性能(加载速度等等)较(Glide、Fresco)差
- 自身没有实现"本地缓存"
Glide Google 开源
支持 Gif WebP Video
生命周期继承
高效缓存策略
- 支持Memory和Disk缓存
- Picasso 只会缓存原始尺寸图片,而Glide缓存时多种规格
- 内存开销小,默认 RGB_565,Picasso 默认是 ARGB_8888
缺点:
- 使用方法复杂,实现方法较多
- 使用比Fresco简单,但性能(加载&缓存)却比不上 Fresco
Fresco Facebook
- 大大减少OOM,底层使用C++技术解决图片缓存问题
- 使用加单,几乎全部功能都能在 xml 上定制
缺点:
- 用法变得更加复杂
- 依赖包更大了 2~3M
- 底层C++,阅读源码困难
对比项 | Picasso | Glide | Fresco |
---|---|---|---|
地址 | github.com/square/pica… | github.com/bumptech/gl… | github.com/facebook/fr… |
发布时间 | 2013年5月 | 2014年9月 | 2015年5月 |
是否支持gif | false | true | true |
是否支持webP | true | true | true |
视频缩略图 | false | true | true |
大小 | 100k | 500 KB | 2~3M |
加载速度 | 中 | 高 | 高 |
Disk+Men Cache | true | true | true |
Easy of use | low | mediun | difficult |
star | 13160 | 14709 | 12444 |
开发者 | Square主导 | Google主导 | Facebook主导 |
详细属性对比
对比项 | Glide | Fresco |
---|---|---|
配置 | compile 'com.github.bumptech.glide:glide:XXX.XXX' | compile 'com.facebook.fresco:fresco:XXX.XXX |
初始化 | 直接使用 | Fresco.initialize(this); |
layout | 普通ImageView | 独有的SimpleDraweeView |
圆角, 圆形 | 需要自己实现圆角,继承自BitmapTransformation操作bitmap对象实现 | 通过RoundingParams设置参数 |
缓存 | Glide内存和磁盘缓存 | 三级缓存,分别是 Bitmap缓存,未解码图片缓存, 文件缓存。 |
缓存图像大小 | Glide则会根据ImageView控件尺寸获得对应的大小的bitmap来展示,从而缓存也可以针对不同的对象:原始图像(source),结果图像(result) | 缓存原始图像 |
加载策略 | Glide只有占位图 | 先加载小尺寸图片,再加载大尺寸的 |
加载进度 | false | true |
Bitmap myBitmap = Glide.with(上下文)
.load(url)
.asBitmap() //必须
.get()
//同样在DataSubscriber中获取
FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(url));
if (resource != null && resource.getFile() != null) {
setImage(ImageSource.uri(Uri.fromFile(resource.getFile())));
}
2、LRUCache原理
LruCache DiskLruCache
LruCache是Android 3.1所提供的一个缓存类DisLruCache目前在Android还不是Android SDK的一部分,但Android官方文档推荐使用该算法来实现硬盘缓存。
LinkedHashMap 它使用了一个双向链表来存储Map中的Entry顺序关系,这种顺序有两种,一种是LRU顺序,一种是插入顺序
put()重要的就是在添加过缓存对象后,调用trimToSize()方法,来判断缓存是否已满,如果满了就要删除近期最少使用的算法。
trimToSize()方法不断地删除LinkedHashMap中队头的元素,即近期最少访问的,直到缓存大小小于最大值
LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队头元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队尾。
3、图片加载原理
4、自己去实现图片库,怎么做?
图片的同步加载
图片的异步加载
图片压缩
内存缓存
磁盘缓存
网络拉取
5、Glide源码解析
RequestManager with(Context context)
RequestManager with(android.app.Activity)
RequestManager with(android.app.Fragment)
RequestManager with(android.support.v4.app.Fragment)
RequestManager with(android.support.v4.app.FragmentActivity)
无论使用什么参数,最终都会进入如下三个方法创建 RequestManager:
RequestManager fragmentGet(Context context, android.app.FragmentManager fm);
RequestManager supportFragmentGet(Context context, android.support.v4.app.FragmentManager fm);
RequestManager getApplicationManager(Context context);
结论:
- Activity--FragmentManager--RequestManagerFragment--RequestManager,所以一个 Activity 对应一个 RequestManager
- 一个 Fragment 对应一个 RequestManager
- Activity 包含 Fragment,Fragment 包含 Fragment,若分别创建 Glide 请求是并不会只创建一个 RequestManager 的
- 子线程发起 Glide 请求或传入对象为 ApplicationContext ,则使用全局单例的 RequestManager
创建 RequestBuilder 的 load 方法有很多:
RequestBuilder<Drawable> load(@Nullable Bitmap bitmap);
RequestBuilder<Drawable> load(@Nullable Drawable drawable);
RequestBuilder<Drawable> load(@Nullable String string);
RequestBuilder<Drawable> load(@Nullable Uri uri);
RequestBuilder<Drawable> load(@Nullable File file);
RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId);
RequestBuilder<Drawable> load(@Nullable URL url);
RequestBuilder<Drawable> load(@Nullable byte[] model);
RequestBuilder<Drawable> load(@Nullable Object model);
看看有这么多重载方法,没一个都代表不同的加载源。 除此之外还有两个特殊的方法:
RequestBuilder<File> downloadOnly();
RequestBuilder<File> download(@Nullable Object model);
再创建 RequestManager 时会先创建一个不可见的 Fragment ,通过 FM 加入到当前页面,用这个不可见的 Fragment 即可检测页面的生命周期。
Request 主要的实现类有三个:
SingleRequest
ThumbnailRequestCoordinator
ErrorRequestCoordinator
6、Glide使用什么缓存?
BitmapPool 大小通过 MemorySizeCalculator 设置;
使用 LRU 算法维护 BitmapPool ;
Glide 会根据 Bitmap 的大小与 Config 生成一个 Key;
Key 也有自己对应的对象池,使用 Queue 实现;
数据最终存储在 GroupedLinkedMap 中;
GroupedLinkedMap 使用哈希表、循环链表、List 来存储数据。
7、Glide内存缓存如何控制大小?
一种是Resource缓存,一类是Bitmap缓存。
Resource缓存: 图片从网络加载,将图片缓存到本地,当需要再次使用时,直接从缓存中取出而无需再次请求网络。
Glide在缓存Resource使用三层缓存,包括:
一级缓存:缓存被回收的资源,使用LRU算法(Least Frequently Used,最近最少使用算法)。当需要再次使用到被回收的资源,直接从内存返回。
二级缓存:使用弱引用缓存正在使用的资源。当系统执行gc操作时,会回收没有强引用的资源。使用弱引用缓存资源,既可以缓存正在使用的强引用资源,也不阻碍系统需要回收无引用资源。
三级缓存:磁盘缓存。网络图片下载成功后将以文件的形式缓存到磁盘中。
Bitmap缓存 通过Bitmap压缩质量参数:Glide默认使用RGB_565,比系统默认使用的ARGB_8888节省一半的资源,但RGB_565无法显示透明度。
Bitmap缓存算法:
在Glide中,使用BitmapPool来缓存Bitmap,使用的也是LRU算法。
当需要使用Bitmap时,从Bitmap的池子中取出合适的Bitmap,若取不到合适的,则再新创建。
当Bitmap使用完后,不直接调用Bitmap.recycler()回收,而是放入Bitmap的池子。
8.Fresco 源码分析
初始化Fresco。
获取DataSource。
绑定Controller与Hierarchy。
从内存缓存/磁盘缓存/网络获取图片,并设置到对应的Drawable层。
缓冲缓存层:由BufferedDiskCache实现,提供缓冲功能。
文件缓存层:由DiskStorageCache实现,提供实际的缓存功能。
文件存储层:由DefaultDiskStorage实现,提供磁盘文件读写的功能。
DiskStorageCache:实现了FileCache接口与DiskTrimmable接口是缓存的主要实现类。
DefaultDiskStorage:实现了DiskStorage接口,封装了磁盘IO的读写逻辑。
BufferedDiskCache:在DiskStorageCache的基础上提供了Buffer功能。
private static final String CONTENT_FILE_EXTENSION = ".cnt"; private static final String TEMP_FILE_EXTENSION = ".tmp";
未解码图片内存缓存:由EncodedImage描述真正的缓存对象。 已解码图片内存缓存:由BitmapMemoryCache描述真正的缓存对象。