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() 方法删除元素。
相关推荐
还鮟4 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡5 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi005 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil7 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你7 小时前
Android View的绘制原理详解
android
移动开发者1号10 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号10 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best15 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk15 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭20 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin