前言
近期研究了一下Glide的图片加载框架,在这里和大家分享一下。由于代码研读有限,难免有错误的地方,了解的童鞋还望指正。学习小组QQ群: 193765960。
本篇是Glide框架及源码解析的第三篇,更多文章敬请关注后续文章。如果这篇文章对大家学习Glide有帮助,还望大家多多转载。
版权归作者所有,如有转发,请注明文章出处:http://www.jianshu.com/u/d43d948bef39
相关文章:
跟着源码学设计:Glide框架及源码解析(一)
跟着源码学设计:Glide框架及源码解析(二)
跟着源码学设计:Glide框架及源码解析(三)
跟着源码学设计:Glide框架及源码解析(四)
跟着源码学设计:Glide框架及源码解析(五)
Glide内存缓存机制
在之前的两篇中我们剖析了Glide的生命周期绑定机制和Glide的请求管理机制。接下来按说应该讲到request实际请求资源并回调刷新界面这一块了,但是为了更好的理解Glide在这一块的设计,我先大致的讲一讲Glide的内存缓存和管理机制。
不同于其他常见网络加载框架只有LruCatch一种缓存机制,Glide内存为三块(非常牛逼巧妙的设计):
- ActiveResourceCache:缓存当前正在使用的资源(注意是弱引用)
- LruResourceCache: 缓存最近使用过但是当前未使用的资源,LRU算法
- BitmapPool:缓存所有被释放的图片,内存复用,LRU算法
注意:
- LruResourceCache和ActiveResourceCache设计是为了尽可能的资源复用
- BitmapPool的设计目的是为了尽可能的内存复用
说的比较抽象,是不是懵逼了?别急,上图:
Glide内存缓存及管理机制
-
当我们需要显示某个资源时,Glide会先去查找LruResourceCache,找到了则将资源从LruResourceCache移除加入到ActiveResourceCache;
-
LruResourceCache找不到资源则查找ActiveResourceCache。
-
如果在ActiveResourceCache也找不到合适的资源,则会根据加载策略从硬盘或者网络加载资源。
-
获取数据后Glide会从BitmapPool中找寻合适的可供内存复用的废弃recycled bitmap(找不到则会重新创建bitmap对象),然后刷新bitmap的数据。
-
bitmap被转换封装为Resource缓存入ActiveResourceCache和Request对象中。
-
Request的target会获取resource中引用的bitmap并展示。
-
当target的资源需要release时,resource会根据缓存策略被缓存到LruResourceCache,同时ActiveResourceCache中的弱引用会被删除。如果,该资源不能缓存到LruResourceCache,则资源将被recycle到BitmapPool。
-
当需要回收内存时(比如系统内存不足或者生命周期结束),LruResourceCache将根据LRU算法recycle一些resource到BitmapPool。
-
BitmapPool会根据缓存池的尺寸和recycled resource的缓存策略来缓存resource的bitmap。
-
BitmapPool会根据LRU算法和缓存池的尺寸来释放一些老旧资源。
-
当系统GC时,则会回收可回收的资源释放内存
这样就完成了一个资源的完整的循环。
BitmapPool的内存复用机制
知识储备:
- BitmapFactory.Options.inBitmap是AndroiD3.0新增的一个属性,如果设置了这个属性则会重用这个Bitmap的内存从而提升性能。
- 在SDK 11 -> 18之间,重用的bitmap大小必须是一致的,例如给inBitmap赋值的图片大小为100-100,那么新申请的bitmap必须也为100-100才能够被重用。从SDK 19开始,新申请的bitmap大小必须小于或者等于已经赋值过的bitmap大小。
- 新申请的bitmap与旧的bitmap必须有相同的解码格式,例如大家都是8888的,如果前面的bitmap是8888,那么就不能支持4444与565格式的bitmap了
使用inbitmap前,内存占用情况
使用inbitmap前,内存占用情况使用inbitmap后,内存占用情况
使用inbitmap后,内存占用情况下面看一下核心代码:Downsampler的downsampleWithSize()方法
private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
Bitmap.Config config = getConfig(is, decodeFormat);
options.inSampleSize = sampleSize;
options.inPreferredConfig = config;
if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
}
return decodeStream(is, bufferedStream, options);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static void setInBitmap(BitmapFactory.Options options, Bitmap recycled) {
if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) {
options.inBitmap = recycled;
}
}
让我们先看一下我们最常见到的LruMemoryCache机制
ActiveResourceCache的设计- 如图,当系统内存不足时,LruMemoryCache会根据LRU算法移除一些资源(bitmap)
- 针对移除的资源,系统在GC时会回收资源(bitmap)以释放内存
- 当应用再次需要次资源时,需要重新分配内存,重新对资源文件进行解析生成bitmap
1)这样会造成内存抖动;
2)比较耗费时间,影响流畅度(GC也比较频繁)
让我们再来看一下Glide的机制
ActiveResourceCache的设计- 如图,当系统内存不足时,LruResourceCache会根据LRU算法移除一些资源(resource)到BitmapPool
- 到BitmapPool会根据LRU算法移除一些资源(bitmap)
- 当应用再次需要资源时,会优先复用到BitmapPool中的bitmap对象(复用其内存),只需刷新bitmap的像素数据
1)这样能有效地降低内存抖动;
2)由于很多情况下可以复用废弃bitmap的内存,因此避免了内存分配等造成的性能损耗,系统比较流畅
3)降低了系统GC的频率
4)LruResourceCache和BitmapPool中都是当前不在使用的资源,做整体的资源回收那叫一个酸爽。
(本篇是Glide框架及源码解析的第三篇,更多文章敬请关注后续文章。版权归作者所有,如有转发,请注明文章出处:原文链接)
网友评论