DiskLruCache

来源:互联网 发布:推荐系统算法 编辑:程序博客网 时间:2024/04/30 10:44

官方源码

https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java
https://github.com/JakeWharton/DiskLruCache

相关博客:

http://blog.csdn.net/guolin_blog/article/details/28863651
http://blog.csdn.net/lmj623565791/article/details/47251585
http://blog.csdn.net/cauchyweierstrass/article/details/50687778

基本使用

  1. 打开

    DiskLruCache diskLruCache= open(File directory, int appVersion, int valueCount, long maxSize);
  2. 存储

    DiskLruCache.Editor editor = diskLruCache.edit(key);OuputStream ouputStream = editor.newOutputStream(0);

    然后往该ouputStream中写入收即可。如果是写入字符串可以使用,其实就是封装了往输出流中写的代码。

    editor.set(int index, String value);

  3. 访问

    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  if (snapShot != null) {      InputStream is = snapShot.getInputStream(0);  }

    如果是文本,可以直接获取

    snapShot.getString(int index)
  4. 已缓存的大小 size()

  5. 关闭 close(), 不应再有后续操作

  6. 清空缓存 delete()

源码注释

public final class DiskLruCache implements Closeable {  static final String JOURNAL_FILE = "journal";  static final String JOURNAL_FILE_TEMP = "journal.tmp";  static final String JOURNAL_FILE_BACKUP = "journal.bkp";  static final String MAGIC = "libcore.io.DiskLruCache";  static final String VERSION_1 = "1";  static final long ANY_SEQUENCE_NUMBER = -1;  static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}");  private static final String CLEAN = "CLEAN";  private static final String DIRTY = "DIRTY";  private static final String REMOVE = "REMOVE";  private static final String READ = "READ";    /*     * This cache uses a journal file named "journal". A typical journal file     * looks like this:     *  日志文件格式     *     *     libcore.io.DiskLruCache  // 文件标识     *     1  // 版本,常量     *     100 // 应用版本,自己传     *     2 // 每个 key 对应几个文件     *     // 空行     *    状态 key 可选的特定状态值     *     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054     *     DIRTY 335c4c6028171cfddfbaae1a9c313c52     *     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342     *     REMOVE 335c4c6028171cfddfbaae1a9c313c52     *     DIRTY 1ab96a171faeeee38496d8b330771a7a     *     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234     *     READ 335c4c6028171cfddfbaae1a9c313c52     *     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6     *     * The first five lines of the journal form its header. They are the     * constant string "libcore.io.DiskLruCache", the disk cache's version,     * the application's version, the value count, and a blank line.     * 每行是一条缓存的记录,空格分隔     * Each of the subsequent lines in the file is a record of the state of a     * cache entry. Each line contains space-separated values: a state, a key,     * and optional state-specific values.     *   o DIRTY lines track that an entry is actively being created or updated.     *     Every successful DIRTY action should be followed by a CLEAN or REMOVE     *     action. DIRTY lines without a matching CLEAN or REMOVE indicate that     *     temporary files may need to be deleted.     *    DIRTY 活动的,创建或更新中,紧接着 成功 会写入一行CLEAN, 失败 REMOVE     *     *   o CLEAN lines track a cache entry that has been successfully published     *     and may be read. A publish line is followed by the lengths of each of     *     its values.     *     *     CLEAN 成功写入的,可能被读,后面是值的长度     *   o READ lines track accesses for LRU.     *     *     READ 一次读取记录     *     *   o REMOVE lines track entries that have been deleted.     *     *    REMOVE 已被删除的记录     *     * The journal file is appended to as cache operations occur. The journal may     * occasionally be compacted by dropping redundant lines. A temporary file named     * "journal.tmp" will be used during compaction; that file should be deleted if     * it exists when the cache is opened.     */  private final File directory; // 缓存目录  private final File journalFile; // 缓存日志文件  private final File journalFileTmp;// 临时日志文件  private final File journalFileBackup;// 日志备份文件  private final int appVersion;  private long maxSize; // 最大容量  private final int valueCount; // 一个key 对应几个值,一般为1  private long size = 0;// 当前容量  private Writer journalWriter;  private final LinkedHashMap<String, Entry> lruEntries =      new LinkedHashMap<String, Entry>(0, 0.75f, true); // lru 的实际实现,重点是第三个参数  private int redundantOpCount; // 操作数,2000时会重置并重建日志  /**   * To differentiate between old and current snapshots, each entry is given   * a sequence number each time an edit is committed. A snapshot is stale if   * its sequence number is not equal to its entry's sequence number.   */  private long nextSequenceNumber = 0;  /** This cache uses a single background thread to evict entries. */  // 单一线程用于计算大小,重置日志等  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 一条缓存对应几个值   * @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();        cache.journalWriter = new BufferedWriter(            new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII));        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;  }  // 读日志  // remove 删除了,read 不处理,lruEntries 中剩余 clean  dirty read  private void readJournal() throws IOException {    StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);    // 检查文件头    try {      String magic = reader.readLine();      String version = reader.readLine();      String appVersionString = reader.readLine();      String valueCountString = reader.readLine();      String blank = reader.readLine();      if (!MAGIC.equals(magic)          || !VERSION_1.equals(version)          || !Integer.toString(appVersion).equals(appVersionString)          || !Integer.toString(valueCount).equals(valueCountString)          || !"".equals(blank)) {        throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "            + valueCountString + ", " + blank + "]");      }      int lineCount = 0;      while (true) { // 遍历日志        try {          readJournalLine(reader.readLine());          lineCount++;        } catch (EOFException endOfJournal) {          break;        }      }      // 记录多余操作数      redundantOpCount = lineCount - lruEntries.size();    } finally {      Util.closeQuietly(reader);    }  }  // 处理一行数据,脏数据 currentEditor 有值  private void readJournalLine(String line) throws IOException {    int firstSpace = line.indexOf(' ');    if (firstSpace == -1) {      throw new IOException("unexpected journal line: " + line);    }    int keyBegin = firstSpace + 1;    int secondSpace = line.indexOf(' ', keyBegin);    final String key;    if (secondSpace == -1) { // dirty read remove      key = line.substring(keyBegin);      // 删除 remove      if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {        lruEntries.remove(key);        return; // 结束      }    } else { // clean 的 key      key = line.substring(keyBegin, secondSpace);    }    Entry entry = lruEntries.get(key); // 移动到队尾    if (entry == null) {      entry = new Entry(key);      lruEntries.put(key, entry);    }    // clean    if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) {      String[] parts = line.substring(secondSpace + 1).split(" ");      entry.readable = true;      entry.currentEditor = null;      entry.setLengths(parts);    } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) {      entry.currentEditor = new Editor(entry); // 设置 editor    } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) {      // This work was already done by calling lruEntries.get().    } else {      throw new IOException("unexpected journal line: " + line);    }  }  /**   * Computes the initial size and collects garbage as a part of opening the   * cache. Dirty entries are assumed to be inconsistent and will be deleted.   */  private void processJournal() throws IOException {    deleteIfExists(journalFileTmp);    for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {      Entry entry = i.next();      if (entry.currentEditor == null) {        for (int t = 0; t < valueCount; t++) {          size += entry.lengths[t]; // 记录 可用文件 大小        }      } else { // 删除脏数据        entry.currentEditor = null;        for (int t = 0; t < valueCount; t++) {          deleteIfExists(entry.getCleanFile(t));          deleteIfExists(entry.getDirtyFile(t));        }        i.remove();      }    }  }  /**   * Creates a new journal that omits redundant information. This replaces the   * current journal if it exists.   */  private synchronized void rebuildJournal() throws IOException {    if (journalWriter != null) {      journalWriter.close();    }    Writer writer = new BufferedWriter(        new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII));    // 将文件头写入临时文件    try {      writer.write(MAGIC);      writer.write("\n");      writer.write(VERSION_1);      writer.write("\n");      writer.write(Integer.toString(appVersion));      writer.write("\n");      writer.write(Integer.toString(valueCount));      writer.write("\n");      writer.write("\n");      for (Entry entry : lruEntries.values()) {        if (entry.currentEditor != null) {          writer.write(DIRTY + ' ' + entry.key + '\n');        } else {          writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');        }      }    } finally {      writer.close();    }    // 将原日志重命名为备份    if (journalFile.exists()) {      renameTo(journalFile, journalFileBackup, true);    }    // 临时文件转为日志文件    renameTo(journalFileTmp, journalFile, false);    journalFileBackup.delete();    journalWriter = new BufferedWriter(        new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));  }  private static void deleteIfExists(File file) throws IOException {    if (file.exists() && !file.delete()) {      throw new IOException();    }  }  private static void renameTo(File from, File to, boolean deleteDestination) throws IOException {    if (deleteDestination) {      deleteIfExists(to);    }    if (!from.renameTo(to)) {      throw new IOException();    }  }  /**   * Returns a snapshot of the entry named {@code key}, or null if it doesn't   * exist is not currently readable. If a value is returned, it is moved to   * the head of the LRU queue.   */  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);  }  /**   * Returns an editor for the entry named {@code key}, or null if another   * edit is in progress.   *   * // 正在处理返回 null   */  public Editor edit(String key) throws IOException {    return edit(key, ANY_SEQUENCE_NUMBER);  }  private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {    checkNotClosed();// 流没关闭    validateKey(key);// 验证 key 名    Entry entry = lruEntries.get(key);    if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null        || entry.sequenceNumber != expectedSequenceNumber)) {      return null; // Snapshot is stale.    }    if (entry == null) {      entry = new Entry(key);      lruEntries.put(key, entry);    } else if (entry.currentEditor != null) {      return null; // Another edit is in progress.    }    Editor editor = new Editor(entry);    entry.currentEditor = editor;    // Flush the journal before creating files to prevent file leaks.    // 正在编辑    journalWriter.write(DIRTY + ' ' + key + '\n');    journalWriter.flush();    return editor;  }  /** Returns the directory where this cache stores its data. */  public File getDirectory() {    return directory;  }  /**   * Returns the maximum number of bytes that this cache should use to store   * its data.   */  public synchronized long getMaxSize() {    return maxSize;  }  /**   * Changes the maximum number of bytes the cache can store and queues a job   * to trim the existing store, if necessary.   * // 重置最大容量,可能会引起 trim   */  public synchronized void setMaxSize(long maxSize) {    this.maxSize = maxSize;    executorService.submit(cleanupCallable);  }  /**   * Returns the number of bytes currently being used to store the values in   * this cache. This may be greater than the max size if a background   * deletion is pending.   */  public synchronized long size() {    return size;  }  private synchronized void completeEdit(Editor editor, boolean success) throws IOException {    Entry entry = editor.entry;    if (entry.currentEditor != editor) {      throw new IllegalStateException();    }    // If this edit is creating the entry for the first time, every index must have a value.    if (success && !entry.readable) {      for (int i = 0; i < valueCount; i++) {        if (!editor.written[i]) {          editor.abort();          throw new IllegalStateException("Newly created entry didn't create value for index " + i);        }        if (!entry.getDirtyFile(i).exists()) {          editor.abort();          return;        }      }    }    for (int i = 0; i < valueCount; i++) {      File dirty = entry.getDirtyFile(i);      if (success) {        if (dirty.exists()) {          File clean = entry.getCleanFile(i);          dirty.renameTo(clean);          long oldLength = entry.lengths[i];          long newLength = clean.length();          entry.lengths[i] = newLength;          size = size - oldLength + newLength;        }      } else {        deleteIfExists(dirty);      }    }    redundantOpCount++;    entry.currentEditor = null;    if (entry.readable | success) {      entry.readable = true;      journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');      if (success) {        entry.sequenceNumber = nextSequenceNumber++;      }    } else {// 失败后移除数据      lruEntries.remove(entry.key);      journalWriter.write(REMOVE + ' ' + entry.key + '\n');    }    journalWriter.flush();    if (size > maxSize || journalRebuildRequired()) {      executorService.submit(cleanupCallable);    }  }  /**   * We only rebuild the journal when it will halve the size of the journal   * and eliminate at least 2000 ops.   * 是否需要重建日志   *    */  private boolean journalRebuildRequired() {    final int redundantOpCompactThreshold = 2000;    return redundantOpCount >= redundantOpCompactThreshold //        && redundantOpCount >= lruEntries.size();  }  /**   * 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;  }  /** Returns true if this cache has been closed. */  public synchronized boolean isClosed() {    return journalWriter == null;  }  private void checkNotClosed() {    if (journalWriter == null) {      throw new IllegalStateException("cache is closed");    }  }  /** Force buffered operations to the filesystem. */  public synchronized void flush() throws IOException {    checkNotClosed();    trimToSize();    journalWriter.flush();  }  /** 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;  }  private void trimToSize() throws IOException {    while (size > maxSize) {      Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();      remove(toEvict.getKey());    }  }  /**   * Closes the cache and deletes all of its stored values. This will delete   * all files in the cache directory including files that weren't created by   * the cache.   */  public void delete() throws IOException {    close();    Util.deleteContents(directory);  }  private void validateKey(String key) {    Matcher matcher = LEGAL_KEY_PATTERN.matcher(key);    if (!matcher.matches()) {      throw new IllegalArgumentException("keys must match regex [a-z0-9_-]{1,64}: \"" + key + "\"");    }  }  private static String inputStreamToString(InputStream in) throws IOException {    return Util.readFully(new InputStreamReader(in, Util.UTF_8));  }  /** A snapshot of the values for an entry. */  public final class Snapshot implements Closeable {    private final String key;    private final long sequenceNumber;    private final InputStream[] ins;    private final long[] lengths;    private Snapshot(String key, long sequenceNumber, InputStream[] ins, long[] lengths) {      this.key = key;      this.sequenceNumber = sequenceNumber;      this.ins = ins;      this.lengths = lengths;    }    /**     * Returns an editor for this snapshot's entry, or null if either the     * entry has changed since this snapshot was created or if another edit     * is in progress.     */    public Editor edit() throws IOException {      return DiskLruCache.this.edit(key, sequenceNumber);    }    /** Returns the unbuffered stream with the value for {@code index}. */    public InputStream getInputStream(int index) {      return ins[index];    }    /** Returns the string value for {@code index}. */    public String getString(int index) throws IOException {      return inputStreamToString(getInputStream(index));    }    /** Returns the byte length of the value for {@code index}. */    public long getLength(int index) {      return lengths[index];    }    public void close() {      for (InputStream in : ins) {        Util.closeQuietly(in);      }    }  }  private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() {    @Override    public void write(int b) throws IOException {      // Eat all writes silently. Nom nom.    }  };  /** Edits the values for an entry. */  public final class Editor {    private final Entry entry;    private final boolean[] written;    private boolean hasErrors;    private boolean committed;    private Editor(Entry entry) {      this.entry = entry;      this.written = (entry.readable) ? null : new boolean[valueCount];    }    /**     * Returns an unbuffered input stream to read the last committed value,     * or null if no value has been committed.     */    public InputStream newInputStream(int index) throws IOException {      synchronized (DiskLruCache.this) {        if (entry.currentEditor != this) {          throw new IllegalStateException();        }        if (!entry.readable) {          return null;        }        try {          return new FileInputStream(entry.getCleanFile(index));        } catch (FileNotFoundException e) {          return null;        }      }    }    /**     * Returns the last committed value as a string, or null if no value     * has been committed.     */    public String getString(int index) throws IOException {      InputStream in = newInputStream(index);      return in != null ? inputStreamToString(in) : null;    }    /**     * Returns a new unbuffered output stream to write the value at     * {@code index}. If the underlying output stream encounters errors     * when writing to the filesystem, this edit will be aborted when     * {@link #commit} is called. The returned output stream does not throw     * IOExceptions.     */    public OutputStream newOutputStream(int index) throws IOException {      synchronized (DiskLruCache.this) {        if (entry.currentEditor != this) {          throw new IllegalStateException();        }        if (!entry.readable) {          written[index] = true;        }        File dirtyFile = entry.getDirtyFile(index); // 获取key.tmp 临时文件        FileOutputStream outputStream;        try {          outputStream = new FileOutputStream(dirtyFile);        } catch (FileNotFoundException e) {          // Attempt to recreate the cache directory.          directory.mkdirs(); //          try {            outputStream = new FileOutputStream(dirtyFile);          } catch (FileNotFoundException e2) {            // We are unable to recover. Silently eat the writes.            return NULL_OUTPUT_STREAM;          }        }        return new FaultHidingOutputStream(outputStream);      }    }    /** Sets the value at {@code index} to {@code value}. */    public void set(int index, String value) throws IOException {      Writer writer = null;      try {        writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8);        writer.write(value);      } finally {        Util.closeQuietly(writer);      }    }    /**     * Commits this edit so it is visible to readers.  This releases the     * edit lock so another edit may be started on the same key.     */    public void commit() throws IOException {      if (hasErrors) {        completeEdit(this, false);        remove(entry.key); // The previous entry is stale.      } else {        completeEdit(this, true);      }      committed = true;    }    /**     * Aborts this edit. This releases the edit lock so another edit may be     * started on the same key.     */    public void abort() throws IOException {      completeEdit(this, false);    }    public void abortUnlessCommitted() {      if (!committed) {        try {          abort();        } catch (IOException ignored) {        }      }    }    // 屏蔽 exception,记录错误    private class FaultHidingOutputStream extends FilterOutputStream {      private FaultHidingOutputStream(OutputStream out) {        super(out);      }      @Override public void write(int oneByte) {        try {          out.write(oneByte);        } catch (IOException e) {          hasErrors = true;        }      }      @Override public void write(byte[] buffer, int offset, int length) {        try {          out.write(buffer, offset, length);        } catch (IOException e) {          hasErrors = true;        }      }      @Override public void close() {        try {          out.close();        } catch (IOException e) {          hasErrors = true;        }      }      @Override public void flush() {        try {          out.flush();        } catch (IOException e) {          hasErrors = true;        }      }    }  }  private final class Entry {    private final String key;    /** Lengths of this entry's files. */    private final long[] lengths;    /** True if this entry has ever been published. */    private boolean readable;    /** The ongoing edit or null if this entry is not being edited. */    private Editor currentEditor;    /** The sequence number of the most recently committed edit to this entry. */    private long sequenceNumber;    private Entry(String key) {      this.key = key;      this.lengths = new long[valueCount];    }    public String getLengths() throws IOException {      StringBuilder result = new StringBuilder();      for (long size : lengths) {        result.append(' ').append(size);      }      return result.toString();    }    /** Set lengths using decimal numbers like "10123". */    private void setLengths(String[] strings) throws IOException {      if (strings.length != valueCount) {        throw invalidLengths(strings);      }      try {        for (int i = 0; i < strings.length; i++) {          lengths[i] = Long.parseLong(strings[i]);        }      } catch (NumberFormatException e) {        throw invalidLengths(strings);      }    }    private IOException invalidLengths(String[] strings) throws IOException {      throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings));    }    public File getCleanFile(int i) {      return new File(directory, key + "." + i);    }    public File getDirtyFile(int i) {      return new File(directory, key + "." + i + ".tmp");    }  }}
0 0
原创粉丝点击