图片缓存策略

众所周知,基于客户端app的开发中都会涉及到大量的图片,包括在线或者本地内置的,而对于在线图片的读取如果都实施从网络上读,会造成大量流量的浪费并且交互非常糟糕。所以对于已经读取过的在线图片,需要在本地有一些缓存以便快速读取展现给用户,而本地缓存主要策略包括:
内存缓存+sd卡缓存双缓存机制

内存缓存策略LruCache:Least Recently Used最近最少使用算法即会淘汰最近最少使用的数据,可以看看源码:

public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;

    /** Size of this cache in units. Not necessarily the number of elements. */
    private int size;
    private int maxSize;

    private int putCount;
    private int createCount;
    private int evictionCount;
    private int hitCount;
    private int missCount;
    /**
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *     the maximum number of entries in the cache. For all other caches,
     *     this is the maximum sum of the sizes of the entries in this cache.
     */
    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);
    }
    ...
}

可以看到LruCache的构造函数可以指定缓存的最大容量,并初始化了一个LinkedHashMap,也就是LruCache主要依赖LinkedHashMap实现的核心算法

LruCache关键方法有:从缓存取数据get、向缓存存数据put、移除最近最少使用的数据trimToSize方法,下面依次看这三个方法

    /**
     * Returns the value for {@code key} if it exists in the cache or can be
     * created by {@code #create}. If a value was returned, it is moved to the
     * head of the queue. This returns null if a value is not cached and cannot
     * be created.
     */
    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++;
        }

        /*
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         */

        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;
        }
    }

1、从map取数据 map存在对应的value则直接返回value

2、从map取数据 map不存在则去create一个对应的数据并put到map中,重新计算大小之后调用trimToSize方法,删除访问次数最少的元素

    /**
     * Caches {@code value} for {@code key}. The value is moved to the head of
     * the queue.
     *
     * @return the previous value mapped by {@code key}.
     */
    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;
    }

将键值对放入map,重新计算大小之后调用trimToSize方法,删除访问次数最少的元素

    /**
     * Remove the eldest entries until the total of remaining entries is at or
     * below the requested size.
     *
     * @param maxSize the maximum size of the cache before returning. May be -1
     *            to evict even 0-sized elements.
     */
    public void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }

                if (size <= maxSize) {
                    break;
                }

                Map.Entry<K, V> toEvict = map.eldest();
                if (toEvict == null) {
                    break;
                }

                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

1、当前size小于maxSize 不做任何操作

2、当前size大于等于maxSize 取出最近最少使用的数据移除并调整size

sd卡缓存策略DiskLruCache:

源码参考:
https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java

源码详解:
http://blog.csdn.net/lmj623565791/article/details/47251585

DiskLruCache缓存框架中有一个关键的文件journal文件,这个文件会存储所有的读取操作记录

journal文件格式

libcore.io.DiskLruCache
1
1
1

DIRTY c3bac86f2e7a291a1a200b853835b664
CLEAN c3bac86f2e7a291a1a200b853835b664 4698
READ c3bac86f2e7a291a1a200b853835b664
DIRTY c59f9eec4b616dc6682c7fa8bd1e061f
CLEAN c59f9eec4b616dc6682c7fa8bd1e061f 4698
READ c59f9eec4b616dc6682c7fa8bd1e061f
DIRTY be8bdac81c12a08e15988555d85dfd2b
CLEAN be8bdac81c12a08e15988555d85dfd2b 99
READ be8bdac81c12a08e15988555d85dfd2b
DIRTY 536788f4dbdffeecfbb8f350a941eea3
REMOVE 536788f4dbdffeecfbb8f350a941eea3 
  • 第一行固定字符串libcore.io.DiskLruCache
  • 第二行DiskLruCache的版本号,源码中为常量1
  • 第三行为你的app的版本号
  • 第四行指每个key对应几个文件,一般为1
  • 第五行,空行

以上5行可以称为该文件的文件头,DiskLruCache初始化的时候,如果该文件存在需要校验该文件头。

接下来的行,可以认为是操作记录。

DIRTY 表示一个entry正在被写入(其实就是把文件的OutputStream交给你了)。那么写入分两种情况,如果成功会紧接着写入一行CLEAN的记录;如果失败,会增加一行REMOVE记录。

REMOVE除了上述的情况呢,当你自己手动调用remove(key)方法的时候也会写入一条REMOVE记录。

READ就是说明有一次读取的记录。

每个CLEAN的后面还记录了文件的长度,注意可能会一个key对应多个文件,那么就会有多个数字(参照文件头第四行)。
从这里看出,只有CLEAN且没有REMOVE的记录,才是真正可用的Cache Entry记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值