Android LruCache源码分析

文章目录

Android LruCache源码分析

概述

LruCache(Least Recently Used Cache,最近最少使用缓存)是 Android 中的一种缓存机制。

根据数据的使用频率淘汰减少使用的数据,当需要缓存新数据时,如果缓存已满,LruCache 会淘汰最近最少使用的数据,腾出空间给新数据。

LruCache和LinkedHashMap关系

LruCache 内部使用的是 LinkedHashMap,这是因为 LinkedHashMap 的构造函数里有个布尔参数 accessOrder,当它为 true 时,LinkedHashMap 会以访问顺序的方式排列元素,如下:

java 复制代码
Map<Integer, Integer> map = new LinkedHashMap<>(5, 0.75F, true);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.put(4, 4);
map.put(5, 5);
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
    System.out.println(entry.getValue());
}

/*
 * 1
 * 2
 * 3
 * 4
 * 5
 */
java 复制代码
// 访问2个元素
map.get(3); 
map.get(4);
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
    System.out.println(entry.getValue());
}

/*
 * 1
 * 2
 * 5
 * 3
 * 4
 */

最近访问的2个元素被移动到尾部,LruCache 也是从尾部访问数据,在表头删除数据。

源码分析

写入数据

java 复制代码
public final V put(K key, V value) {
    // 如果值为null,则抛出异常
    if (key == null || value == null) {
        throw new NullPointerException("key == null || value == null");
    }

    V previous;

    // 加锁,线程安全
    synchronized (this) {
        // 写入计数
        putCount++;
        // 通过sizeOf()计算当前项的大小,并累加已有缓存大小
        size += safeSizeOf(key, value);
        // 写入操作
        previous = map.put(key, value);
        // 如果previous为null表示为新增数据,如果previous不为null表示为修改数据
        if (previous != null) {
            // 修改数据需要将size恢复到以前的大小
            size -= safeSizeOf(key, previous);
        }
    }

    // 回调entryRemoved()方法
    if (previous != null) {
        entryRemoved(false, key, previous, value);
    }

    // 调整缓存大小
    trimToSize(maxSize);
    return previous;
}

// 调整缓存大小
public void trimToSize(int maxSize) {
    // 死循环
    while (true) {
        K key;
        V value;
        synchronized (this) {
            // 缓存未满,直接返回
            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()方法
        entryRemoved(true, key, value, null);
    }
}
  • 插入元素,并增加已缓存的大小。
  • 调用 trimToSize() 方法,调整缓存大小。

读取数据

java 复制代码
public final V get(@NonNull K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V mapValue;
    synchronized (this) {
        // 获取元素,LinkedHashMap会将这个元素移动到表尾
        mapValue = map.get(key);
        if (mapValue != null) {
            hitCount++;
            return mapValue;
        }
        missCount++;
    }

    // 没有元素时,会回调create()方法
    V createdValue = create(key);
    if (createdValue == null) {
        return null;
    }

    // 下面和put()流程相同
    synchronized (this) {
        createCount++;
        mapValue = map.put(key, createdValue);

        if (mapValue != null) {
            map.put(key, mapValue);
        } else {
            size += safeSizeOf(key, createdValue);
        }
    }

    if (mapValue != null) {
        entryRemoved(false, key, createdValue, mapValue);
        return mapValue;
    } else {
        trimToSize(maxSize);
        return createdValue;
    }
}
  • 最终调用 LinkedHashMap#get() 方法,因为accessOrder为true ,因此元素会移动到表尾。
  • 如果没有获取到元素时,会调用 create() 方法创建元素,接着执行put()流程。

删除缓存

java 复制代码
public final V remove(@NonNull K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V previous;
    synchronized (this) {
        // 调用LinkedHashMap#remove()方法删除元素
        previous = map.remove(key);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {
        entryRemoved(false, key, previous, null);
    }

    return previous;
}
  • 调用 LinkedHashMap#remove() 方法删除元素。
相关推荐
运维Z叔1 小时前
云安全 | AWS S3存储桶安全设计缺陷分析
android·网络·网络协议·tcp/ip·安全·云计算·aws
Reese_Cool3 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言
平凡シンプル3 小时前
安卓 uniapp跨端开发
android·uni-app
elina80133 小时前
安卓实现导入Excel文件
android·excel
严文文-Chris3 小时前
【设计模式-享元】
android·java·设计模式
趋势大仙4 小时前
SQLiteDatabase insert or replace数据不生效
android·数据库
DS小龙哥4 小时前
QT For Android开发-打开PPT文件
android·qt·powerpoint
试行5 小时前
Android实现自定义下拉列表绑定数据
android·java
Dingdangr9 小时前
Android中的Intent的作用
android
技术无疆9 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入