LruCache+DiskLruCache详解

一.LruCache类

Glide的内存缓存内部使用的是LruCache类

 

LruCache类源码

public class LruCache<T, Y> {
    private final LinkedHashMap<T, Y> cache = new LinkedHashMap<T, Y>(100, 0.75f, true);
    private int maxSize;
    private final int initialMaxSize;
    private int currentSize = 0;

    /**
     * Constructor for LruCache.
     *
     * @param size The maximum size of the cache, the units must match the units used in {@link #getSize(Object)}.
     */
    public LruCache(int size) {
        this.initialMaxSize = size;
        this.maxSize = size;
    }

    /**
     * Sets a size multiplier that will be applied to the size provided in the constructor to set the new size of the
     * cache. If the new size is less than the current size, entries will be evicted until the current size is less
     * than or equal to the new size.
     *
     * @param multiplier The multiplier to apply.
     */
    public void setSizeMultiplier(float multiplier) {
        if (multiplier < 0) {
            throw new IllegalArgumentException("Multiplier must be >= 0");
        }
        maxSize = Math.round(initialMaxSize * multiplier);
        evict();
    }

    /**
     * Returns the size of a given item, defaulting to one. The units must match those used in the size passed in to the
     * constructor. Subclasses can override this method to return sizes in various units, usually bytes.
     *
     * @param item The item to get the size of.
     */
    protected int getSize(Y item) {
        return 1;
    }

    /**
     * A callback called whenever an item is evicted from the cache. Subclasses can override.
     *
     * @param key The key of the evicted item.
     * @param item The evicted item.
     */
    protected void onItemEvicted(T key, Y item) {
        // optional override
    }

    /**
     * Returns the current maximum size of the cache in bytes.
     */
    public int getMaxSize() {
        return maxSize;
    }

    /**
     * Returns the sum of the sizes of all items in the cache.
     */
    public int getCurrentSize() {
        return currentSize;
    }

    /**
     * Returns true if there is a value for the given key in the cache.
     *
     * @param key The key to check.
     */

    public boolean contains(T key) {
        return cache.containsKey(key);
    }

    /**
     * Returns the item in the cache for the given key or null if no such item exists.
     *
     * @param key The key to check.
     */
    public Y get(T key) {
        return cache.get(key);
    }

    /**
     * Adds the given item to the cache with the given key and returns any previous entry for the given key that may
     * have already been in the cache.
     *
     * <p>
     *     If the size of the item is larger than the total cache size, the item will not be added to the cache and
     *     instead {@link #onItemEvicted(Object, Object)} will be called synchronously with the given key and item.
     * </p>
     *
     * @param key The key to add the item at.
     * @param item The item to add.
     */
    public Y put(T key, Y item) {
        final int itemSize = getSize(item);
        if (itemSize >= maxSize) {
            onItemEvicted(key, item);
            return null;
        }

        final Y result = cache.put(key, item);
        if (item != null) {
            currentSize += getSize(item);
        }
        if (result != null) {
            // TODO: should we call onItemEvicted here?
            currentSize -= getSize(result);
        }
        evict();

        return result;
    }

    /**
     * Removes the item at the given key and returns the removed item if present, and null otherwise.
     *
     * @param key The key to remove the item at.
     */
    public Y remove(T key) {
        final Y value = cache.remove(key);
        if (value != null) {
            currentSize -= getSize(value);
        }
        return value;
    }

    /**
     * Clears all items in the cache.
     */
    public void clearMemory() {
        trimToSize(0);
    }

    /**
     * Removes the least recently used items from the cache until the current size is less than the given size.
     *
     * @param size The size the cache should be less than.
     */
    protected void trimToSize(int size) {
        Map.Entry<T, Y> last;
        while (currentSize > size) {
            last = cache.entrySet().iterator().next();
            final Y toRemove = last.getValue();
            currentSize -= getSize(toRemove);
            final T key = last.getKey();
            cache.remove(key);
            onItemEvicted(key, toRemove);
        }
    }

    private void evict() {
        trimToSize(maxSize);
    }
}

 

源码分析

源码可知,LruCache类中使用了LinkedHashMap实现内存缓存。

 

LinkedHashMap有一个三个参数的构造方法

public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

参数1 initialCapacity:初始化LinkedHashMap的大小。

参数2 loadFactor:负载因子。

参数3 accessOrder:按照什么顺序向LinkedHashMap中插入数据。true:按照最近访问量的高低排序,false:按照插入顺序排序。

 

LruCache类中使用LinkedHashMap时传参如下

private final LinkedHashMap<T, Y> cache = new LinkedHashMap<T, Y>(100, 0.75f, true);

也就是说LruCache类中使用LinkedHashMap时

<1> 初始化值大小:100。

<2> 加载因子:0.75f。

<3> 向LinkedHashMap中插入数据:按照最近访问量的高低排序。

 

 

 

 

 

 

 

二.DiskLruCache

Glide的磁盘缓存内部使用的是DiskLruCache类

 

DiskLruCache类部分源码

public final class DiskLruCache implements Closeable {
  
 
  private final LinkedHashMap<String, Entry> lruEntries =new LinkedHashMap<String, Entry>(0, 0.75f, true);
  
  final ThreadPoolExecutor executorService =
      new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
  private final Callable<Void> cleanupCallable = new Callable<Void>() {
    public Void call() throws Exception {
      synchronized (DiskLruCache.this) {
        if (journalWriter == null) {
          return null; // Closed.
        }
        trimToSize();
        if (journalRebuildRequired()) {
          rebuildJournal();
          redundantOpCount = 0;
        }
      }
      return null;
    }
  };

  private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
    this.directory = directory;
    this.appVersion = appVersion;
    this.journalFile = new File(directory, JOURNAL_FILE);
    this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
    this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
    this.valueCount = valueCount;
    this.maxSize = maxSize;
  }

  /**
   * Opens the cache in {@code directory}, creating a cache if none exists
   * there.
   *
   * @param directory a writable directory
   * @param valueCount the number of values per cache entry. Must be positive.
   * @param maxSize the maximum number of bytes this cache should use to store
   * @throws IOException if reading or writing the cache directory fails
   */

  public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
      throws IOException {
    if (maxSize <= 0) {
      throw new IllegalArgumentException("maxSize <= 0");
    }
    if (valueCount <= 0) {
      throw new IllegalArgumentException("valueCount <= 0");
    }

    // If a bkp file exists, use it instead.
    File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
    if (backupFile.exists()) {
      File journalFile = new File(directory, JOURNAL_FILE);
      // If journal file also exists just delete backup file.
      if (journalFile.exists()) {
        backupFile.delete();
      } else {
        renameTo(backupFile, journalFile, false);
      }
    }

    // Prefer to pick up where we left off.
    DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
    if (cache.journalFile.exists()) {
      try {
        cache.readJournal();
        cache.processJournal();
        return cache;
      } catch (IOException journalIsCorrupt) {
        System.out
            .println("DiskLruCache "
                + directory
                + " is corrupt: "
                + journalIsCorrupt.getMessage()
                + ", removing");
        cache.delete();
      }
    }

    // Create a new empty cache.
    directory.mkdirs();
    cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
    cache.rebuildJournal();
    return cache;
  }

}

 

源码分析

源码可知,DiskLruCache类中也使用了LinkedHashMap实现磁盘缓存。

 

1.DiskLruCache类中使用LinkedHashMap时传参如下

private final LinkedHashMap<String, Entry> lruEntries =new LinkedHashMap<String, Entry>(0, 0.75f, true);

也就是说DiskLruCache类中使用LinkedHashMap时

<1> 初始化值大小:0。

<2> 加载因子:0.75f。

<3> 向LinkedHashMap中插入数据:按照最近访问量的高低排序。

 

 

2.Glide的磁盘缓存还用到了ThreadPoolExecutor(线程池核心类)

final ThreadPoolExecutor executorService =new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
参数 corePoolSize(核心线程的大小):0


参数 maximumPoolSize(线程池中允许的最大线程数):1


参数 keepAliveTime(线程保持存活的时间):60


参数 TimeUnit(参数keepAliveTime的时间单位):秒


参数 workQueue(线程池中的任务队列): LinkedBlockingQueue:这个队列接收到任务的时候,如果当前已经创建的核心线程数小于线程池的核心线程数上限,则新建线程(核心线程)处理任务;如果当前已经创建的核心线程数等于核心线程数上限,则进入队列等待


参数 threadFactory(线程工厂):默认


参数 RejectedExecutionHandler(表示当拒绝处理任务时的策略):默认 这是当任务队列和线程池都满了时所采取的应对策略,默认是AbordPolicy, 表示无法处理新任务,并抛出 RejectedExecutionException 异常

 

使用submit方式提交(submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果)

executorService.submit(cleanupCallable);

 

 

 

附:线程池详解

https://blog.csdn.net/weixin_37730482/article/details/72781767

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值