Android 源码解析-LruCache 缓存工具类

关于Android的三级缓存,其中主要的就是内存缓存和硬盘缓存。这两种缓存机制的实现都应用到了LruCache算法,今天我们就从使用到源码解析,来彻底理解Android中的缓存机制。

1、LruCache的介绍

LruCache是Android API 12以后提供的一个缓存工具类,采用了最近最少使用算法。它把最近使用的对象用“强引用”存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前就从内存中移除,并提供了get和put方法来完成缓存的获取和添加操作。

2、LruCache的使用

以图片缓存为例

private LruCache<String, Bitmap> mMemoryCache;

public MemoryCache() {

    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    int cacheSize = maxMemory / 4;

    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
        }
    };
}

第一步 设置缓存大小,一般为当前进程可用容量的1/8。
第二步 重写sizeOf方法,计算出要缓存的每张图片的大小。

3、LruCache 的实现原理

LruCache 使用LinkedHashMap维护一个列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。如下图

这里写图片描述

LinkedHashMap中双向链表的结构是访问顺序实现,accessOrder = ture

LinkedHashMap<Integer, Integer> linkedHashMap = new LinkedHashMap<>(10, 0.75f, true);

linkedHashMap.put(0, 0);
linkedHashMap.put(1, 1);
linkedHashMap.put(2, 2);
linkedHashMap.put(3, 3);
linkedHashMap.put(4, 4);
linkedHashMap.get(1);
linkedHashMap.get(2);
linkedHashMap.put(5, 5);

for (Map.Entry<Integer, Integer> entry : linkedHashMap.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

4、LruCache 的源码解析

构造方法与属性变量
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;    //put放大调用的次数
private int createCount; //create 的次数
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);
}
put 方法
/**
 * 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) { //key 和 value 不能为null
        throw new NullPointerException("key == null || value == null");
    }

    V previous;
    synchronized (this) {
        putCount++;  //put 后值加一
        size += safeSizeOf(key, value); //增加当前缓存的大小
        previous = map.put(key, value); //加入缓存对象
        if (previous != null) { // 如果cache 中存在对象则恢复到之前的大小
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {  //空算法
        entryRemoved(false, key, previous, value);
    }

    trimToSize(maxSize);  //调整缓存大小
    return previous;
}
trimToSize(int maxSize)
/**
 * 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) { //如果当前的size 小于最大size 跳出循环
                break;
            }

            Map.Entry<K, V> toEvict = map.eldest(); //取队尾的元素,近期最少访问的元素
            if (toEvict == null) {
                break;
            }

            key = toEvict.getKey();
            value = toEvict.getValue();
            map.remove(key); //删除队尾的元素并更新当前size 的大小
            size -= safeSizeOf(key, value);
            evictionCount++;
        }

        entryRemoved(true, key, value, null);
    }
}
get(K key)
/**
 * 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); //实现被访问的元素更新到队头部的功能 具体实现看LinkedHashMap的实现方法
        if (mapValue != null) {
            hitCount++;
            return mapValue;
        }
        missCount++;
    }

这样我们就完成了对LruCache的使用,现在我们发现这个缓存类也没那么复杂,用法也十分简单,正是因为简单,所以我们可以很方便的对其进行扩展。当然了,这仅仅是做了内存缓存,熟悉缓存机制的朋友一定会知道磁盘缓存和内存缓存二者的关系,有关磁盘缓存的问题我将在以后的文章中进行讲述。

Android系统下载管理DownloadManager增强方法,可用于包括获取下载相关信息,如: getStatusById(long) 得到下载状态 getDownloadBytes(long) 得到下载进度信息 getBytesAndStatus(long) 得到下载进度信息和状态 getFileName(long) 得到下载文件路径 getUri(long) 得到下载uri getReason(long) 得到下载失败或暂停原因 getPausedReason(long) 得到下载暂停原因 getErrorCode(long) 得到下载错误码 =================================================================== package cn.trinea.android.common.util; import java.lang.reflect.Method; import android.app.DownloadManager; import android.app.DownloadManager.Request; import android.database.Cursor; import android.net.Uri; import android.os.Build; /** * DownloadManagerPro * * Get download info * {@link #getStatusById(long)} get download status * {@link #getDownloadBytes(long)} get downloaded byte, total byte * {@link #getBytesAndStatus(long)} get downloaded byte, total byte and download status * {@link #getFileName(long)} get download file name * {@link #getUri(long)} get download uri * {@link #getReason(long)} get failed code or paused reason * {@link #getPausedReason(long)} get paused reason * {@link #getErrorCode(long)} get failed error code * * * Operate download * {@link #isExistPauseAndResumeMethod()} whether exist pauseDownload and resumeDownload method in * {@link DownloadManager} * {@link #pauseDownload(long...)} pause download. need pauseDownload(long...) method in {@link DownloadManager} * {@link #resumeDownload(long...)} resume download. need resumeDownload(long...) method in {@link DownloadManager} * * * RequestPro * {@link RequestPro#setNotiClass(String)} set noti class * {@link RequestPro#setNotiExtras(String)} set noti extras * * * @author Trinea 2013-5-4 */ public class DownloadManagerPro { public static final Uri CONTENT_URI
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值