当前位置:网站首页>Glide缓存核心原理详解
Glide缓存核心原理详解
2022-08-10 22:13:00 【AD钙奶-lalala】
Glide目前几乎是Android图片框架的不二选择,那么你知道Glide有哪些优点吗?
- 使用简单,链式调用( Glide.with(context).load(url).into(iv) )
- 占用内存较小,默认使用RGB_565,相比较Picasso内存减少一半(RGB_8888)
- 与activity生命周期绑定,避免内存泄漏
- 缓存优化
今天的主题是一起来研究一下Glide的核心原理。
Glide的三级缓存原理是什么?
读取一张图片的顺序:LRU算法缓存->弱引用缓存->磁盘缓存(如果设置了的话)。
我们的APP中想要加载某张图片时,先去LruCache中寻找图片,如果LruCache中有,则直接取出来使用,并将该图片放入WeakReference中,如果LruCache中没有,则去WeakReference中寻找,如果WeakReference中有,则从WeakReference中取出图片使用,如果WeakReference中也没有图片,则从磁盘缓存/网络中加载图片。
注:图片正在使用时存在于 activeResources 弱引用map中。
缓存图片的写入顺序:弱引用缓存->LRU算法缓存->磁盘缓存。
当图片不存在的时候,先从网络下载图片,然后将图片存入弱引用中,glide会采用一个acquired(int)变量用来记录图片被引用的次数,当acquired变量大于0的时候,说明图片正在使用中,也就是将图片放到弱引用缓存当中;如果acquired变量等于0了,说明图片已经不再被使用了,那么此时会调用方法来释放资源,首先会将缓存图片从弱引用中移除,然后再将它put到LruResourceCache当中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不再使用
中的图片使用LruCache来进行缓存的功能。
关于LRUCache:
最近最少使用算法,设定一个缓存大小,当缓存达到这个大小之后,会将最老的数据移除,避免图片占用内存过大导致OOM。LruCache 内部用LinkHashMap存取数据,在双向链表保证数据新旧顺序的前提下,设置一个最大内存,往里面put数据的时候,当数据达到最大内存的时候,将最老的数据移除掉,保证内存不超过设定的最大值。
关于LinkedHashMap:
LinkHashMap 继承HashMap,在 HashMap的基础上,新增了双向链表结构,每次访问数据的时候,会更新被访问的数据的链表指针,具体就是先在链表中删除该节点,然后添加到链表头header之前,这样就保证了链表头header节点之前的数据都是最近访问的(从链表中删除并不是真的删除数据,只是移动链表指针,数据本身在map中的位置是不变的)。
前面说了一堆,其实重点大家也很明白,就是这个LRUCache:
public class LruCache<T, Y> {
private final LinkedHashMap<T, Y> cache =
new LinkedHashMap<T, Y>(100, 0.75f, true);//最后一个传入true,代表基于顺序访问
private int maxSize;
private final int initialMaxSize;
private int currentSize = 0;
public LruCache(int size) {
this.initialMaxSize = size;
this.maxSize = size;
}
public void setSizeMultiplier(float multiplier) {
if (multiplier < 0) {
throw new IllegalArgumentException("Multiplier must be >= 0");
}
maxSize = Math.round(initialMaxSize * multiplier);
evict();
}
protected int getSize(Y item) {
return 1;
}
protected void onItemEvicted(T key, Y item) {
// optional override
}
public int getMaxSize() {
return maxSize;
}
public int getCurrentSize() {
return currentSize;
}
public boolean contains(T key) {
return cache.containsKey(key);
}
public Y get(T key) {
return cache.get(key);
}
public Y put(T key, Y item) {
final int itemSize = getSize(item);
if (itemSize >= maxSize) {
onItemEvicted(key, item);
return null;
}
final Y result = cache.put(key, item);
if (item != null) {
currentSize += getSize(item);
}
if (result != null) {
// TODO: should we call onItemEvicted here?
currentSize -= getSize(result);
}
evict();
return result;
}
public Y remove(T key) {
final Y value = cache.remove(key);
if (value != null) {
currentSize -= getSize(value);
}
return value;
}
public void clearMemory() {
trimToSize(0);
}
protected void trimToSize(int size) {
Map.Entry<T, Y> last;
while (currentSize > size) {
last = cache.entrySet().iterator().next();//基于顺序访问
final Y toRemove = last.getValue();
currentSize -= getSize(toRemove);
final T key = last.getKey();
cache.remove(key);
onItemEvicted(key, toRemove);
}
}
private void evict() {
trimToSize(maxSize);
}
}解释一下一个重要的成员变量maxSize是什么意思:资源的最大字节数。
我们先来分析一下put方法:
- 首先获取资源的字节数itemSize
- 如果itemSize直接超限制,返回,onItemEvicted方法在子类有实现,后面再看
- cache.put将资源加到LinkedHashMap里面
- item != null 将item的size加到currentSize上
- result != null 说明缓存已存在,前面加到currentSize里面去的要再去掉
- 最后调用evict方法
再来分析一下evict(意思是逐出)方法:
- 调用trimToSize
- 遍历获取最后一个资源,移出cache
我们再来看下LRUCache的子类来加深我们的理解:
public class LruResourceCache extends LruCache<Key, Resource<?>>
implements MemoryCache {
private ResourceRemovedListener listener;
/**
* Constructor for LruResourceCache.
*
* @param size The maximum size in bytes the in memory cache can use.
*/
public LruResourceCache(int size) {
super(size);
}
@Override
public void setResourceRemovedListener(ResourceRemovedListener listener) {
this.listener = listener;
}
@Override
protected void onItemEvicted(Key key, Resource<?> item) {
if (listener != null) {
listener.onResourceRemoved(item);
}
}
@Override
protected int getSize(Resource<?> item) {
return item.getSize();
}
@SuppressLint("InlinedApi")
@Override
public void trimMemory(int level) {
if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
// Nearing middle of list of cached background apps
// Evict our entire bitmap cache
clearMemory();
} else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Entering list of cached background apps
// Evict oldest half of our bitmap cache
trimToSize(getCurrentSize() / 2);
}
}
}- getSize是获取资源字节数的意思
- onItemEvicted可以理解为将资源移除
时间有点晚了,后面再用一篇文章来分析一下LinkedHashMap的数据结构,晚安,好梦!
边栏推荐
- shell programming without interaction
- 《DevOps围炉夜话》- Pilot - CNCF开源DevOps项目DevStream简介 - feat. PMC成员胡涛
- 文件IO-缓冲区
- 音乐播放器(未完成版本)
- ThreadLocal comprehensive analysis (1)
- Power system power flow calculation (Newton-Raphson method, Gauss-Seidel method, fast decoupling method) (Matlab code implementation)
- OneNote tutorial, how to organize notebooks in OneNote?
- camera preview process --- from HAL to OEM
- 过滤器
- Shell编程之条件语句(二)
猜你喜欢
随机推荐
《DevOps围炉夜话》- Pilot - CNCF开源DevOps项目DevStream简介 - feat. PMC成员胡涛
美味石井饭菜
什么是Jmeter?Jmeter使用的原理步骤是什么?
今日睡眠质量记录75分
Shell programming specification and variables
罗克韦尔AB PLC RSLogix5000中计数器指令使用方法介绍
BM7 链表中环的入口结点
How many threads does LabVIEW allocate?
Power system power flow calculation (Newton-Raphson method, Gauss-Seidel method, fast decoupling method) (Matlab code implementation)
高数_复习_第5章:多元函数微分学
How to be a Righteous Hacker?What should you study?
STL-stack
BM13判断一个链表是否为回文结构
STL-deque
What is Jmeter? What are the principle steps used by Jmeter?
port forwarding
商家招募电商主播要考虑哪些内容
What are the concepts, purposes, processes, and testing methods of interface testing?
Redis
Thread State 详解









