LruCache和DiskCache总结

本文深入分析Android中的LruCache和DiskCache,讲解它们在图片缓存中的作用和实现原理。从内存缓存LruCache的构造、put、get和trimToSize方法,到硬盘缓存DiskCache的创建、写入、查找、移除以及journal文件的作用,完整解释了图片三级缓存的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

日常我们写程序的时候经常会使用到网络的图片,如果我们每次都去网上加载,那么性能难免会差一些,并且网络情况并不是总是 那么好,那么这时候我们就需要使用缓存了,我们学习android都知道图片的三级缓存,分别是内存缓存,硬盘缓存,网络缓存。

它的大体流程是这样的,给定一个网址,加载一张图片

  1. 如果内存缓存中存在,那就取出来,放上去,如果没有就找硬盘缓存
  2. 如果硬盘缓存中存在,那就取出来,放上去,并添加到内存缓存中,如果没有就请求网络
  3. 请求网络,把图片放上去,存一份到内存缓存和硬盘缓存中

接下来我们来分析一下到底是怎么实现的

一、LruCache

LruCache使用的是LRU算法,也叫最近最少使用算法,就是不断往里面存东西,超过上限,把最近最少的对象先淘汰,DiskCache使用的也是该算法。

LruCache的使用很简单

int maxMemory = (int) (Runtime.getRuntime().totalMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }
};

首先得到程序最大可使用的内存空间,然后计算出内存缓存使用的空间,通常设置为最大可用内存的八分之一,然后实例化一个LruCache对象,和HashMap一样,因为里面就是用LinkHashMap实现的(之后会讲),需要指定键值对类型,传入缓存可用空间大小,并实现sizeof方法,对存入的对象的大小进行计算。

源码分析

1.构造函数

    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

实例化的时候传入的可用空间大小在构造函数中被赋值给成员变量,并示例化了一个LinkHashMap,起始容量为0,负载因子为0.75,并将accessOrder设置为了true

public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

LinkHashMap默认是插入顺序的,当把accessOrder设置为true的时候就变成了访问顺序。

public V get(Object key) {
    LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
    if (e == null)
        return null;
    e.recordAccess(this);
    return e.value;
}

当LinkHashMap调用get方法时会调用recordAccess方法 

void recordAccess(HashMap<K,V> m) {
    LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
    if (lm.accessOrder) {
        lm.modCount++;
        remove();
        addBefore(lm.header);
    }
}

该方法会将该元素删除并添加到队列头部

2.put方法

public final V put(K key, V value) {
    //不可为空,否则抛出异常
    if (key == null || value == null) {
        throw new NullPointerException("key == null || value == null");
    }
    V previous;
    synchronized (this) {
        //插入的缓存对象值加1
        putCount++;
        //增加已有缓存的大小
        size += safeSizeOf(key, value);
        //向map中加入缓存对象
        previous = map.put(key, value);
        //如果已有缓存对象,则缓存大小恢复到之前
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }
    //entryRemoved()是个空方法,可以自行实现
    if (previous != null) {
        entryRemoved(false, key, previous, value);
    }
    //调整缓存大小(关键方法)
    trimToSize(maxSize);
    return previous;
}

put方法主要做了三件事,第一计算当前已用空间,第二讲对象存入,第三调整缓存

3.trimToSize方法

public void trimToSize(int maxSize) {
    //死循环
    while (true) {
        K key;
        V value;
        synchronized (this) {
            //如果map为空并且缓存size不等于0或者缓存size小于0,抛出异常
            if (size < 0 || (map.isEmpty() && size != 0)) {
                throw new IllegalStateException(getClass().getName()+ ".sizeOf() is reporting inconsistent results!");
            }
            //如果缓存大小size小于最大缓存,或者map为空,不需要再删除缓存对象,跳出循环
            if (size <= maxSize || map.isEmpty()) {
            break;
            }
            //迭代器获取第一个对象,即队尾的元素,近期最少访问的元素
            Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
            key = toEvict.getKey();
            value = toEvict.getValue();
            //删除该对象,并更新缓存大小
            map.remove(key);
            size -= safeSizeOf(key, value);
            evictionCount++;
        }
        entryRemoved(true, key, value, null);
    }
}

在该方法中会不断取队尾的元素进行移除操作,直到当前缓存大小小于最大缓存空间大小。

4.get方法

public final V get(K key) {
    //key为空抛出异常
    if (key == null) {
        throw new NullPoin
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幽蓝丶流月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值