微信号:appjiagou

介绍:分享最有价值的APP技术干货文章,做一个有逼格的APP架构师,拒绝平庸,打造最有价值的APP社区!

Android DiskLruCache 源码解析 硬盘缓存的绝佳方案(下)

2016-12-31 09:43 APP架构师

五、读取缓存

public synchronized Snapshot get(String key) throws IOException {
    checkNotClosed();
    validateKey(key);
    Entry entry = lruEntries.get(key);    if (entry == null) {      
       return null;    }    if (!entry.readable) {      
       return null;    }    // Open all streams eagerly to guarantee that we see a single published    // snapshot. If we opened streams lazily then the streams could come    // from different edits.    InputStream[] ins = new InputStream[valueCount];    try {      for (int i = 0; i < valueCount; i++) {        ins[i] = new FileInputStream(entry.getCleanFile(i));      }    } catch (FileNotFoundException e) {      
       // A file must have been deleted manually!      for (int i = 0; i < valueCount; i++) {        
       if (ins[i] != null) {          Util.closeQuietly(ins[i]);        } else {          
           break;        }      }      
           return null;    }    redundantOpCount++;    journalWriter.append(READ + ' ' + key + '\n');    
           if (journalRebuildRequired()) {      executorService.submit(cleanupCallable);    }    return new Snapshot(key, entry.sequenceNumber, ins, entry.lengths);  }
get方法比较简单,如果取到的为null,或者readable=false,则返回null.否则将cleanFile的FileInputStream进行封装返回Snapshot,且写入一条READ语句。

然后getInputStream就是返回该FileInputStream了。


好了,到此,我们就分析完成了创建DiskLruCache,存入缓存和取出缓存的源码。

除此以外,还有一些别的方法我们需要了解的。



六、其他方法



remove()

/**
   * Drops the entry for {@code key} if it exists and can be removed. Entries
   * actively being edited cannot be removed.
   *
   * @return true if an entry was removed.
   */
  public synchronized boolean remove(String key) throws IOException {
    checkNotClosed();
    validateKey(key);
    Entry entry = lruEntries.get(key);    if (entry == null || entry.currentEditor != null) {      return false;
    }    for (int i = 0; i < valueCount; i++) {
      File file = entry.getCleanFile(i);      if (file.exists() && !file.delete()) {        throw new IOException("failed to delete " + file);
      }
      size -= entry.lengths[i];
      entry.lengths[i] = 0;
    }

    redundantOpCount++;
    journalWriter.append(REMOVE + ' ' + key + '\n');
    lruEntries.remove(key);    if (journalRebuildRequired()) {
      executorService.submit(cleanupCallable);
    }    return true;
  }


如果实体存在且不在被编辑,就可以直接进行删除,然后写入一条REMOVE记录。

与open对应还有个remove方法,大家在使用完成cache后可以手动关闭。

close()

/** Closes this cache. Stored values will remain on the filesystem. */
  public synchronized void close() throws IOException {    if (journalWriter == null) {      return; // Already closed.
    }    for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {      if (entry.currentEditor != null) {
        entry.currentEditor.abort();
      }
    }
    trimToSize();
    journalWriter.close();
    journalWriter = null;
  }


关闭前,会判断所有正在编辑的实体,调用abort方法,最后关闭journalWriter。至于abort方法,其实我们分析过了,就是存储失败的时候的逻辑:


public void abort() throws IOException {
     completeEdit(this, false);
   }


到此,我们的整个源码分析就结束了。可以看到DiskLruCache,利用一个journal文件,保证了保证了cache实体的可用性(只有CLEAN的可用),且获取文件的长度的时候可以通过在该文件的记录中读取。利用FaultHidingOutputStream对FileOutPutStream很好的对写入文件过程中是否发生错误进行捕获,而不是让用户手动去调用出错后的处理方法。其内部的很多细节都很值得推敲。


不过也可以看到,存取的操作不是特别的容易使用,需要大家自己去操作文件流,但在存储比较小的数据的时候(不存在内存问题),很多时候还是希望有类似put(key,value),getAsT(key)等方法直接使用。我看了ASimpleCache 提供的API属于比较好用的了。于是萌生想法,对DiskLruCache公开的API进行扩展,对外除了原有的存取方式以外,提供类似ASimpleCache那样比较简单的API用于存储,而内部的核心实现,依然是DiskLruCache原本的。

github地址: base-diskcache,欢迎star,fork。


 
APP架构师 更多文章 Android DiskLruCache 源码解析 硬盘缓存的绝佳方案(上) Android 沉浸式状态栏攻略 让你的状态栏变色吧 Android干货框架集锦,搭建项目必不可少 国内Top500Android应用分析报告 【推荐】 Android 开源或分享的国外开发者及公司
猜您喜欢 实战明星投票活动四天三夜(技术宅 PK 脑残粉) 深度理解HTTP与RESTFull API 你适合微服务么:实施微服务的4个先决条件和重点工作 券商、期货、私募大佬齐聚恒生首届技术开放日,精彩等你来看! Ceph命令函数调用栈