11月更文挑战|Android基础-LRU缓存策略

577 阅读2分钟

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

前言

缓存策略在Android开发是比较常见的,尤其是在图片使用业务场景中缓存策略发挥重要作用。对于移动应用常用UI就是图片组件而且图片显示资源常常来自网络,因此为了更快的图片加载和减省流量就需要使用缓存策略实现一个完备的图片加载框架。

图片加载库的基本逻辑如下:优先从内存中找图片资源;然后从本地资源找图片;最后两者都没有的情况下才从网络中请求图片资源。

flowchart TD 
A[请求图片加载] --> B{是否有内存缓存};
B -->|是| C[直接加载内存图片资源];  
B ---->|否| D{是否有本地缓存};
D -->|是| E[加载本地图片资源];  
D ---->|否| F[请求加载网络图片资源];
C --> G[图片加载完成];
F --> G[图片加载完成];
E --> G[图片加载完成];

内存缓存策略

关于内存缓存策略,在Android原生代码中有LruCache类。LruCache它的核心缓存策略算法是LRU(Least Recently Used)当缓存超出设置最大值时,会优先删除近期使用最少的缓存对象。当然本地存储缓存也能参考LRU策略,两者相结合就能实现一套如上图展示的完善基础资源缓存策略。

LruCache

LruCache是个泛型类,内部主要采用LinkedHashMap<K, V>存储形式缓存对象。提供put和get操作,当缓存超出最大存储值时会移除使用较少的缓存对象,put新的缓存对象。另外还支持remove方法,主动移除缓存对象释放更多缓存存储值。

  • get
public final V get(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }
    V mapValue;
    synchronized (this) {
        mapValue = map.get(key);
        if (mapValue != null) {
            hitCount++;
            return mapValue;
        }
        missCount++;
    }
    V createdValue = create(key);
    if (createdValue == null) {
        return null;
    }
    synchronized (this) {
        createCount++;
        mapValue = map.put(key, createdValue);

        if (mapValue != null) {
            // There was a conflict so undo that last put
            map.put(key, mapValue);
        } else {
            size += safeSizeOf(key, createdValue);
        }
    }

    if (mapValue != null) {
        entryRemoved(false, key, createdValue, mapValue);
        return mapValue;
    } else {
        trimToSize(maxSize);
        return createdValue;
    }
}
  • 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) {
        putCount++;
        size += safeSizeOf(key, value);
        previous = map.put(key, value);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

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

    trimToSize(maxSize);
    return previous;
}
  • remove
public final V remove(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V previous;
    synchronized (this) {
        previous = map.remove(key);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

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

    return previous;
}

通过put和remove方法会发现不管是添加还是删除都会执行safeSizeOf方法,在safeSizeOf中需要开发者自行实现sizeOf方法计算缓存大小累加到缓存池当中。另外put方法还多了trimToSize方法用来check缓存池是否超出最大缓存值。