当前位置:网站首页>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的数据结构,晚安,好梦!
边栏推荐
猜你喜欢
[Maui official version] Create a cross-platform Maui program, as well as the implementation and demonstration of dependency injection and MVVM two-way binding
阿里云新增三大高性能计算解决方案,助力生命科学行业快速发展
音乐播放器(未完成版本)
Regular expression of shell programming and text processor
CFdiv2-Beautiful Mirrors-(期望)
《DevOps围炉夜话》- Pilot - CNCF开源DevOps项目DevStream简介 - feat. PMC成员胡涛
使用 Cloudreve 搭建私有云盘
交换机和生成树知识点
元宇宙社交应用,靠什么吸引用户「为爱发电」?
威纶通触摸屏如何在报警的同时,显示出异常数据的当前值?
随机推荐
边缘与云计算:哪种解决方案更适合您的连接设备?
JS use regular expressions in g model and non g difference
高通平台开发系列讲解(应用篇)QCMAP应用框架介绍
【640. 求解方程】
VLAN huawei 三种模式
Shell 编程--Sed
OneNote tutorial, how to organize notebooks in OneNote?
What are the concepts, purposes, processes, and testing methods of interface testing?
带着昇腾去旅行:一日看尽金陵城里的AI胜景
LabVIEW分配多少线程?
ASCII、Unicode和UTF-8
如何成为一名正义黑客?你应该学习什么?
How many threads does LabVIEW allocate?
What is Jmeter? What are the principle steps used by Jmeter?
诺诚健华通过注册:施一公家族身价15亿 高瓴浮亏5亿港元
68: Chapter 6: Develop article services: 1: Content sorting; article table introduction; creating [article] article services;
MySQL:MySQL的集群——主从复制的原理和配置
Why general company will say "go back messages such as" after the end of the interview, rather than just tell the interviewer the result?
财务年报怎样翻译,为什么要选择专业翻译公司?
What would happen if disconnecting during the process of TCP connection?