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() 方法删除元素。
相关推荐
Digitally30 分钟前
2026 年 8 款安卓数据擦除软件和应用对比
android
杨忆32 分钟前
android 11以上 截图工具类
android
粤M温同学1 小时前
Android Studio 中安装 CodeBuddy AI助手
android·ide·android studio
阿拉斯攀登2 小时前
【RK3576 安卓 JNI/NDK 系列 08】RK3576 实战(二):JNI 调用 I2C 驱动读取传感器数据
android·安卓ndk入门·jni方法签名·java调用c++·rk3576底层开发·rk3576 i2c开发
赶路人儿3 小时前
常见的mcp配置
android·adb
符哥20083 小时前
充电桩 WiFi 局域网配网(Android/Kotlin)流程、指令及实例说明文档
android·开发语言·kotlin
没有了遇见4 小时前
Android 项目架构之<用户信息模块>
android
Georgewu5 小时前
如何判断应用在鸿蒙卓易通或者出境易环境下?
android·harmonyos
localbob6 小时前
Pico 4XVR 1.10.13安装包下载与安装教程 ico 4XVR最新版下载、4XVR 1.10.13 APK安装包、Pico VR看电影软件、4XVR完整版安装教程、Pico 4播放器推荐、V
android·vr·vr播放器·vr眼镜播放器下载·pico 4xvr·4xvr下载·pico 4xvr最新版安装包
峥嵘life6 小时前
Android16 EDLA【CTS】CtsConnectivityMultiDevicesTestCases存在fail项
android·学习