盒子
盒子
文章目录
  1. Glide内存缓存机制
  2. BitmapPool的内存复用机制
    1. 使用inbitmap前,内存占用情况
    2. 使用inbitmap后,内存占用情况
  3. 让我们先看一下我们最常见到的LruMemoryCache机制
  4. 让我们再来看一下Glide的机制

跟着源码学设计:Glide框架及源码解析(三)

前言

近期研究了一下Glide的图片加载框架,在这里和大家分享一下。由于代码研读有限,难免有错误的地方,了解的童鞋还望指正。学习小组QQ群: 193765960。

本篇是Glide框架及源码解析的第三篇,更多文章敬请关注后续文章。如果这篇文章对大家学习Glide有帮助,还望大家多多转载。

版权归作者所有,如有转发,请注明文章出处:https://xiaodanchen.github.io/archives/

相关文章:

跟着源码学设计: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()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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框架及源码解析的第三篇,更多文章敬请关注后续文章。版权归作者所有,如有转发,请注明文章出处:原文链接

扫描加群
好好学习,天天向上!