Android4.0以上源码中的DiskLruCache类简介

   1 /*
   2  * Copyright (C) 2011 The Android Open Source Project
   3  *
   4  * Licensed under the Apache License, Version 2.0 (the "License");
   5  * you may not use this file except in compliance with the License.
   6  * You may obtain a copy of the License at
   7  *
   8  *      http://www.apache.org/licenses/LICENSE-2.0
   9  *
  10  * Unless required by applicable law or agreed to in writing, software
  11  * distributed under the License is distributed on an "AS IS" BASIS,
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  * See the License for the specific language governing permissions and
  14  * limitations under the License.
  15  */
  16 
  17 
  18 package com.example.android.bitmapfun.util;
  19 
  20 
  21 import java.io.BufferedInputStream;
  22 import java.io.BufferedWriter;
  23 import java.io.Closeable;
  24 import java.io.EOFException;
  25 import java.io.File;
  26 import java.io.FileInputStream;
  27 import java.io.FileNotFoundException;
  28 import java.io.FileOutputStream;
  29 import java.io.FileWriter;
  30 import java.io.FilterOutputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.InputStreamReader;
  34 import java.io.OutputStream;
  35 import java.io.OutputStreamWriter;
  36 import java.io.Reader;
  37 import java.io.StringWriter;
  38 import java.io.Writer;
  39 import java.lang.reflect.Array;
  40 import java.nio.charset.Charset;
  41 import java.util.ArrayList;
  42 import java.util.Arrays;
  43 import java.util.Iterator;
  44 import java.util.LinkedHashMap;
  45 import java.util.Map;
  46 import java.util.concurrent.Callable;
  47 import java.util.concurrent.ExecutorService;
  48 import java.util.concurrent.LinkedBlockingQueue;
  49 import java.util.concurrent.ThreadPoolExecutor;
  50 import java.util.concurrent.TimeUnit;
  51 
  52 
  53 /**
  54  ******************************************************************************
  55  * Taken from the JB source code, can be found in:
  56  * libcore/luni/src/main/java/libcore/io/DiskLruCache.java
  57  * or direct link:
  58  * https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java
  59  ******************************************************************************
  60  *
  61  * A cache that uses a bounded amount of space on a filesystem. Each cache
  62  * entry has a string key and a fixed number of values. Values are byte
  63  * sequences, accessible as streams or files. Each value must be between {@code
  64  * 0} and {@code Integer.MAX_VALUE} bytes in length.
  65  * 一个使用空间大小有边界的文件cache,每一个entry包含一个key和values。values是byte序列,按文件或者流来访问的。
  66  * 每一个value的长度在0---Integer.MAX_VALUE之间。
  67  *
  68  * <p>The cache stores its data in a directory on the filesystem. This
  69  * directory must be exclusive to the cache; the cache may delete or overwrite
  70  * files from its directory. It is an error for multiple processes to use the
  71  * same cache directory at the same time.
  72  * cache使用目录文件存储数据。文件路径必须是唯一的,可以删除和重写目录文件。多个进程同时使用同样的文件目录是不正确的
  73  * 
  74  * <p>This cache limits the number of bytes that it will store on the
  75  * filesystem. When the number of stored bytes exceeds the limit, the cache will
  76  * remove entries in the background until the limit is satisfied. The limit is
  77  * not strict: the cache may temporarily exceed it while waiting for files to be
  78  * deleted. The limit does not include filesystem overhead or the cache
  79  * journal so space-sensitive applications should set a conservative limit.
  80  * cache限制了大小,当超出空间大小时,cache就会后台删除entry直到空间没有达到上限为止。空间大小限制不是严格的,
  81  * cache可能会暂时超过limit在等待文件删除的过程中。cache的limit不包括文件系统的头部和日志,
  82  * 所以空间大小敏感的应用应当设置一个保守的limit大小
  83  *
  84  * <p>Clients call {@link #edit} to create or update the values of an entry. An
  85  * entry may have only one editor at one time; if a value is not available to be
  86  * edited then {@link #edit} will return null.
  87  * <ul>
  88  *     <li>When an entry is being <strong>created</strong> it is necessary to
  89  *         supply a full set of values; the empty value should be used as a
  90  *         placeholder if necessary.
  91  *     <li>When an entry is being <strong>edited</strong>, it is not necessary
  92  *         to supply data for every value; values default to their previous
  93  *         value.
  94  * </ul>
  95  * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
  96  * or {@link Editor#abort}. Committing is atomic: a read observes the full set
  97  * of values as they were before or after the commit, but never a mix of values.
  98  *调用edit()来创建或者更新entry的值,一个entry同时只能有一个editor;如果值不可被编辑就返回null。
  99  *当entry被创建时必须提供一个value。空的value应当用占位符表示。当entry被编辑的时候,必须提供value。
 100  *每次调用必须有匹配Editor commit或abort,commit是原子操作,读必须在commit前或者后,不会造成值混乱。
 101  *
 102  * <p>Clients call {@link #get} to read a snapshot of an entry. The read will
 103  * observe the value at the time that {@link #get} was called. Updates and
 104  * removals after the call do not impact ongoing reads.
 105  * 调用get来读entry的快照。当get调用时读者读其值,更新或者删除不会影响先前的读
 106  *
 107  * <p>This class is tolerant of some I/O errors. If files are missing from the
 108  * filesystem, the corresponding entries will be dropped from the cache. If
 109  * an error occurs while writing a cache value, the edit will fail silently.
 110  * Callers should handle other problems by catching {@code IOException} and
 111  * responding appropriately.
 112  * 该类可以容忍一些I/O errors。如果文件丢失啦,相应的entry就会被drop。写cache时如果error发生,edit将失败。
 113  * 调用者应当相应的处理其它问题
 114  */
 115 public final class DiskLruCache implements Closeable {
 116     static final String JOURNAL_FILE = "journal";
 117     static final String JOURNAL_FILE_TMP = "journal.tmp";
 118     static final String MAGIC = "libcore.io.DiskLruCache";
 119     static final String VERSION_1 = "1";
 120     static final long ANY_SEQUENCE_NUMBER = -1;
 121     private static final String CLEAN = "CLEAN";
 122     private static final String DIRTY = "DIRTY";
 123     private static final String REMOVE = "REMOVE";
 124     private static final String READ = "READ";
 125 
 126 
 127     private static final Charset UTF_8 = Charset.forName("UTF-8");
 128     private static final int IO_BUFFER_SIZE = 8 * 1024;//8K
 129 
 130 
 131     /*
 132      * This cache uses a journal file named "journal". A typical journal file
 133      * looks like this:
 134      *     libcore.io.DiskLruCache
 135      *     1           //the disk cache's version
 136      *     100         //the application's version
 137      *     2           //value count
 138      *
 139      *    //state  key                            optional
 140      *     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
 141      *     DIRTY 335c4c6028171cfddfbaae1a9c313c52
 142      *     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
 143      *     REMOVE 335c4c6028171cfddfbaae1a9c313c52
 144      *     DIRTY 1ab96a171faeeee38496d8b330771a7a
 145      *     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
 146      *     READ 335c4c6028171cfddfbaae1a9c313c52
 147      *     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
 148      *
 149      * The first five lines of the journal form its header. They are the
 150      * constant string "libcore.io.DiskLruCache", the disk cache's version,
 151      * the application's version, the value count, and a blank line.
 152      *
 153      * Each of the subsequent lines in the file is a record of the state of a
 154      * cache entry. Each line contains space-separated values: a state, a key,
 155      * and optional state-specific values.
 156      *   o DIRTY lines track that an entry is actively being created or updated.
 157      *     Every successful DIRTY action should be followed by a CLEAN or REMOVE
 158      *     action. DIRTY lines without a matching CLEAN or REMOVE indicate that
 159      *     temporary files may need to be deleted.
 160      *     Dirty是entry被创建或者更新,每一个dirty应当被clean或remove action,如果有一行dirty没有
 161      *     匹配的clean或Remove action,就表示临时文件需要被删除。
 162      *   o CLEAN lines track a cache entry that has been successfully published
 163      *     and may be read. A publish line is followed by the lengths of each of
 164      *     its values.
 165      *     Clean entry已经成功的发布并且可能会被读过。一个发布行
 166      *   o READ lines track accesses for LRU.
 167      *   Read表示LRU访问
 168      *   o REMOVE lines track entries that have been deleted.
 169      *   Remove表示entry已经被删除
 170      *   
 171      * The journal file is appended to as cache operations occur. The journal may
 172      * occasionally be compacted by dropping redundant lines. A temporary file named
 173      * "journal.tmp" will be used during compaction; that file should be deleted if
 174      * it exists when the cache is opened.
 175      * 日志文件在cache操作发生时添加,日志可能O尔删除的冗余行来压缩。一个临时的名字为journal.tmp的文件将被使用
 176      * 在压缩期间。当cache被opened的时候文件应当被删除。
 177      */
 178 
 179 
 180     private final File directory;
 181     private final File journalFile;//日志文件
 182     private final File journalFileTmp;//日志文件临时文件
 183     private final int appVersion;//应用ersion
 184     private final long maxSize;//最大空间
 185     private final int valueCount;//key对应的value的个数
 186     private long size = 0;
 187     private Writer journalWriter;
 188     private final LinkedHashMap<String, Entry> lruEntries
 189             = new LinkedHashMap<String, Entry>(0, 0.75f, true);
 190     private int redundantOpCount;
 191 
 192 
 193     /**
 194      * To differentiate between old and current snapshots, each entry is given
 195      * a sequence number each time an edit is committed. A snapshot is stale if
 196      * its sequence number is not equal to its entry's sequence number.
 197      * 区分老的和当前的快照,每一个实体在每次编辑被committed时都被赋予一个序列号。
 198      * 一个快照的序列号如果不等于entry的序列号那它就是废弃的。
 199      */
 200     private long nextSequenceNumber = 0;
 201 
 202 
 203     //数组拷贝
 204     /* From java.util.Arrays */
 205     @SuppressWarnings("unchecked")
 206     private static <T> T[] copyOfRange(T[] original, int start, int end) {
 207         final int originalLength = original.length; // For exception priority compatibility.
 208         if (start > end) {
 209             throw new IllegalArgumentException();
 210         }
 211         if (start < 0 || start > originalLength) {
 212             throw new ArrayIndexOutOfBoundsException();
 213         }
 214         final int resultLength = end - start;
 215         final int copyLength = Math.min(resultLength, originalLength - start);
 216         final T[] result = (T[]) Array
 217                 .newInstance(original.getClass().getComponentType(), resultLength);
 218         System.arraycopy(original, start, result, 0, copyLength);
 219         return result;
 220     }
 221 
 222 
 223     /**
 224      * Returns the remainder of 'reader' as a string, closing it when done.
 225      * 返回String的值,然后close
 226      */
 227     public static String readFully(Reader reader) throws IOException {
 228         try {
 229             StringWriter writer = new StringWriter();
 230             char[] buffer = new char[1024];
 231             int count;
 232             while ((count = reader.read(buffer)) != -1) {
 233                 writer.write(buffer, 0, count);
 234             }
 235             return writer.toString();
 236         } finally {
 237             reader.close();
 238         }
 239     }
 240 
 241 
 242     /**
 243      * Returns the ASCII characters up to but not including the next "\r\n", or
 244      * "\n".
 245      *
 246      * @throws java.io.EOFException if the stream is exhausted before the next newline
 247      *     character.
 248      *  读取输入流中返回的某行ASCII码字符
 249      */
 250     public static String readAsciiLine(InputStream in) throws IOException {
 251         // TODO: support UTF-8 here instead
 252 
 253 
 254         StringBuilder result = new StringBuilder(80);
 255         while (true) {
 256             int c = in.read();
 257             if (c == -1) {
 258                 throw new EOFException();
 259             } else if (c == '\n') {
 260                 break;
 261             }
 262 
 263 
 264             result.append((char) c);
 265         }
 266         int length = result.length();
 267         if (length > 0 && result.charAt(length - 1) == '\r') {
 268             result.setLength(length - 1);
 269         }
 270         return result.toString();
 271     }
 272 
 273 
 274     /**
 275      * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
 276      * closeable关闭
 277      */
 278     public static void closeQuietly(Closeable closeable) {
 279         if (closeable != null) {
 280             try {
 281                 closeable.close();
 282             } catch (RuntimeException rethrown) {
 283                 throw rethrown;
 284             } catch (Exception ignored) {
 285             }
 286         }
 287     }
 288 
 289 
 290     /**
 291      * Recursively delete everything in {@code dir}.
 292      * 递归删除dir
 293      */
 294     // TODO: this should specify paths as Strings rather than as Files
 295     public static void deleteContents(File dir) throws IOException {
 296         File[] files = dir.listFiles();
 297         if (files == null) {
 298             throw new IllegalArgumentException("not a directory: " + dir);
 299         }
 300         for (File file : files) {
 301             if (file.isDirectory()) {
 302                 deleteContents(file);
 303             }
 304             if (!file.delete()) {
 305                 throw new IOException("failed to delete file: " + file);
 306             }
 307         }
 308     }
 309 
 310 
 311     /** This cache uses a single background thread to evict entries.
 312      *  后台单线程回收entry  
 313      */
 314     private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
 315             60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
 316     private final Callable<Void> cleanupCallable = new Callable<Void>() {
 317         @Override public Void call() throws Exception {
 318             synchronized (DiskLruCache.this) {
 319                 if (journalWriter == null) {
 320                     return null; // closed
 321                 }
 322                 trimToSize();//回收到满足maxsize
 323                 if (journalRebuildRequired()) {
 324                     rebuildJournal();
 325                     redundantOpCount = 0;
 326                 }
 327             }
 328             return null;
 329         }
 330     };
 331 
 332 
 333     //构造器
 334     private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
 335         this.directory = directory;
 336         this.appVersion = appVersion;
 337         this.journalFile = new File(directory, JOURNAL_FILE);
 338         this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
 339         this.valueCount = valueCount;
 340         this.maxSize = maxSize;
 341     }
 342 
 343 
 344     /**
 345      * Opens the cache in {@code directory}, creating a cache if none exists
 346      * there.
 347      * 创建cache
 348      * 
 349      * @param directory a writable directory
 350      * @param appVersion
 351      * @param valueCount the number of values per cache entry. Must be positive.
 352      * 每一个key相对应的value的数目
 353      * @param maxSize the maximum number of bytes this cache should use to store
 354      * @throws IOException if reading or writing the cache directory fails
 355      */
 356     public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
 357             throws IOException {
 358         if (maxSize <= 0) {//maxsize必须大于0
 359             throw new IllegalArgumentException("maxSize <= 0");
 360         }
 361         if (valueCount <= 0) {//valuecount也必须大于0
 362             throw new IllegalArgumentException("valueCount <= 0");
 363         }
 364 
 365 
 366         // prefer to pick up where we left off优先处理先前的cache
 367         DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
 368         if (cache.journalFile.exists()) {
 369             try {
 370                 cache.readJournal();
 371                 cache.processJournal();
 372                 cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true),
 373                         IO_BUFFER_SIZE);
 374                 return cache;
 375             } catch (IOException journalIsCorrupt) {
 376 //                System.logW("DiskLruCache " + directory + " is corrupt: "
 377 //                        + journalIsCorrupt.getMessage() + ", removing");
 378                 cache.delete();
 379             }
 380         }
 381 
 382 
 383         // create a new empty cache创建一个空新的cache
 384         directory.mkdirs();
 385         cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
 386         cache.rebuildJournal();
 387         return cache;
 388     }
 389 
 390 
 391     //读取日志信息
 392     private void readJournal() throws IOException {
 393         InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE);
 394         try {
 395             String magic = readAsciiLine(in);
 396             String version = readAsciiLine(in);
 397             String appVersionString = readAsciiLine(in);
 398             String valueCountString = readAsciiLine(in);
 399             String blank = readAsciiLine(in);
 400             if (!MAGIC.equals(magic)
 401                     || !VERSION_1.equals(version)
 402                     || !Integer.toString(appVersion).equals(appVersionString)
 403                     || !Integer.toString(valueCount).equals(valueCountString)
 404                     || !"".equals(blank)) {
 405                 throw new IOException("unexpected journal header: ["
 406                         + magic + ", " + version + ", " + valueCountString + ", " + blank + "]");
 407             }
 408 
 409 
 410             while (true) {
 411                 try {
 412                     readJournalLine(readAsciiLine(in));//读取日志信息
 413                 } catch (EOFException endOfJournal) {
 414                     break;
 415                 }
 416             }
 417         } finally {
 418             closeQuietly(in);//关闭输入流
 419         }
 420     }
 421 
 422 
 423     //读取日志中某行日志信息
 424     private void readJournalLine(String line) throws IOException {
 425         String[] parts = line.split(" ");
 426         if (parts.length < 2) {
 427             throw new IOException("unexpected journal line: " + line);
 428         }
 429 
 430 
 431         String key = parts[1];
 432         if (parts[0].equals(REMOVE) && parts.length == 2) {
 433             lruEntries.remove(key);
 434             return;
 435         }
 436 
 437 
 438         Entry entry = lruEntries.get(key);
 439         if (entry == null) {
 440             entry = new Entry(key);
 441             lruEntries.put(key, entry);
 442         }
 443 
 444 
 445         if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) {
 446             entry.readable = true;
 447             entry.currentEditor = null;
 448             entry.setLengths(copyOfRange(parts, 2, parts.length));
 449         } else if (parts[0].equals(DIRTY) && parts.length == 2) {
 450             entry.currentEditor = new Editor(entry);
 451         } else if (parts[0].equals(READ) && parts.length == 2) {
 452             // this work was already done by calling lruEntries.get()
 453         } else {
 454             throw new IOException("unexpected journal line: " + line);
 455         }
 456     }
 457 
 458 
 459     /**
 460      * Computes the initial size and collects garbage as a part of opening the
 461      * cache. Dirty entries are assumed to be inconsistent and will be deleted.
 462      * 处理日志
 463      * 计算初始化cache的初始化大小和收集垃圾。Dirty entry假定不一致将会被删掉。
 464      */
 465     private void processJournal() throws IOException {
 466         deleteIfExists(journalFileTmp);//删除日志文件
 467         for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
 468             Entry entry = i.next();
 469             if (entry.currentEditor == null) {
 470                 for (int t = 0; t < valueCount; t++) {
 471                     size += entry.lengths[t];
 472                 }
 473             } else {
 474                 entry.currentEditor = null;
 475                 for (int t = 0; t < valueCount; t++) {
 476                     deleteIfExists(entry.getCleanFile(t));
 477                     deleteIfExists(entry.getDirtyFile(t));
 478                 }
 479                 i.remove();
 480             }
 481         }
 482     }
 483 
 484 
 485     /**
 486      * Creates a new journal that omits redundant information. This replaces the
 487      * current journal if it exists.
 488      * 创建一个新的删掉冗余信息的日志。替换当前的日志
 489      */
 490     private synchronized void rebuildJournal() throws IOException {
 491         if (journalWriter != null) {
 492             journalWriter.close();
 493         }
 494 
 495 
 496         Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE);
 497         writer.write(MAGIC);
 498         writer.write("\n");
 499         writer.write(VERSION_1);
 500         writer.write("\n");
 501         writer.write(Integer.toString(appVersion));
 502         writer.write("\n");
 503         writer.write(Integer.toString(valueCount));
 504         writer.write("\n");
 505         writer.write("\n");
 506 
 507 
 508         for (Entry entry : lruEntries.values()) {
 509             if (entry.currentEditor != null) {
 510                 writer.write(DIRTY + ' ' + entry.key + '\n');
 511             } else {
 512                 writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
 513             }
 514         }
 515 
 516 
 517         writer.close();
 518         journalFileTmp.renameTo(journalFile);
 519         journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE);
 520     }
 521 
 522 
 523     //文件若存在删除
 524     private static void deleteIfExists(File file) throws IOException {
 525 //        try {
 526 //            Libcore.os.remove(file.getPath());
 527 //        } catch (ErrnoException errnoException) {
 528 //            if (errnoException.errno != OsConstants.ENOENT) {
 529 //                throw errnoException.rethrowAsIOException();
 530 //            }
 531 //        }
 532         if (file.exists() && !file.delete()) {
 533             throw new IOException();
 534         }
 535     }
 536 
 537 
 538     /**
 539      * Returns a snapshot of the entry named {@code key}, or null if it doesn't
 540      * exist is not currently readable. If a value is returned, it is moved to
 541      * the head of the LRU queue.
 542      * 返回key对应的entry的snapshot,当key相应的entry不存在或者当前不可读时返回null。
 543      * 如果返回相应的值,它就会被移动到LRU队列的头部。
 544      */
 545     public synchronized Snapshot get(String key) throws IOException {
 546         checkNotClosed();//检查cache是否已关闭
 547         validateKey(key);//验证key格式的正确性
 548         Entry entry = lruEntries.get(key);
 549         if (entry == null) {
 550             return null;
 551         }
 552 
 553 
 554         if (!entry.readable) {
 555             return null;
 556         }
 557 
 558 
 559         /*
 560          * Open all streams eagerly to guarantee that we see a single published
 561          * snapshot. If we opened streams lazily then the streams could come
 562          * from different edits.
 563          */
 564         InputStream[] ins = new InputStream[valueCount];
 565         try {
 566             for (int i = 0; i < valueCount; i++) {
 567                 ins[i] = new FileInputStream(entry.getCleanFile(i));
 568             }
 569         } catch (FileNotFoundException e) {
 570             // a file must have been deleted manually!
 571             return null;
 572         }
 573 
 574 
 575         redundantOpCount++;
 576         journalWriter.append(READ + ' ' + key + '\n');
 577         if (journalRebuildRequired()) {
 578             executorService.submit(cleanupCallable);
 579         }
 580 
 581 
 582         return new Snapshot(key, entry.sequenceNumber, ins);
 583     }
 584 
 585 
 586     /**
 587      * Returns an editor for the entry named {@code key}, or null if another
 588      * edit is in progress.
 589      */
 590     public Editor edit(String key) throws IOException {
 591         return edit(key, ANY_SEQUENCE_NUMBER);
 592     }
 593 
 594 
 595     //有key和序列号生成一个editor
 596     private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
 597         checkNotClosed();//检查cache关闭与否
 598         validateKey(key);//验证key格式正确性
 599         Entry entry = lruEntries.get(key);
 600         if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
 601                 && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
 602             return null; // snapshot is stale
 603         }
 604         if (entry == null) {
 605             entry = new Entry(key);
 606             lruEntries.put(key, entry);
 607         } else if (entry.currentEditor != null) {
 608             return null; // another edit is in progress
 609         }
 610 
 611 
 612         Editor editor = new Editor(entry);
 613         entry.currentEditor = editor;
 614 
 615 
 616         // flush the journal before creating files to prevent file leaks
 617         journalWriter.write(DIRTY + ' ' + key + '\n');
 618         journalWriter.flush();
 619         return editor;
 620     }
 621 
 622 
 623     /**
 624      * Returns the directory where this cache stores its data.
 625      */
 626     public File getDirectory() {
 627         return directory;
 628     }
 629 
 630 
 631     /**
 632      * Returns the maximum number of bytes that this cache should use to store
 633      * its data.
 634      */
 635     public long maxSize() {
 636         return maxSize;
 637     }
 638 
 639 
 640     /**
 641      * Returns the number of bytes currently being used to store the values in
 642      * this cache. This may be greater than the max size if a background
 643      * deletion is pending.
 644      */
 645     public synchronized long size() {
 646         return size;
 647     }
 648 
 649 
 650     //完成Edit动作
 651     private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
 652         Entry entry = editor.entry;
 653         if (entry.currentEditor != editor) {
 654             throw new IllegalStateException();
 655         }
 656 
 657 
 658         // if this edit is creating the entry for the first time, every index must have a value
 659         if (success && !entry.readable) {
 660             for (int i = 0; i < valueCount; i++) {
 661                 if (!entry.getDirtyFile(i).exists()) {
 662                     editor.abort();
 663                     throw new IllegalStateException("edit didn't create file " + i);
 664                 }
 665             }
 666         }
 667 
 668 
 669         for (int i = 0; i < valueCount; i++) {
 670             File dirty = entry.getDirtyFile(i);
 671             if (success) {
 672                 if (dirty.exists()) {
 673                     File clean = entry.getCleanFile(i);
 674                     dirty.renameTo(clean);
 675                     long oldLength = entry.lengths[i];
 676                     long newLength = clean.length();
 677                     entry.lengths[i] = newLength;
 678                     size = size - oldLength + newLength;
 679                 }
 680             } else {
 681                 deleteIfExists(dirty);
 682             }
 683         }
 684 
 685 
 686         redundantOpCount++;
 687         entry.currentEditor = null;
 688         if (entry.readable | success) {
 689             entry.readable = true;
 690             journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
 691             if (success) {
 692                 entry.sequenceNumber = nextSequenceNumber++;
 693             }
 694         } else {
 695             lruEntries.remove(entry.key);
 696             journalWriter.write(REMOVE + ' ' + entry.key + '\n');
 697         }
 698 
 699 
 700         if (size > maxSize || journalRebuildRequired()) {
 701             executorService.submit(cleanupCallable);
 702         }
 703     }
 704 
 705 
 706     /**
 707      * We only rebuild the journal when it will halve the size of the journal
 708      * and eliminate at least 2000 ops.
 709      * 当日志大小减半并且删掉至少2000项时重新构造日志
 710      */
 711     private boolean journalRebuildRequired() {
 712         final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000;
 713         return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD
 714                 && redundantOpCount >= lruEntries.size();
 715     }
 716 
 717 
 718     /**
 719      * Drops the entry for {@code key} if it exists and can be removed. Entries
 720      * actively being edited cannot be removed.
 721      * 删除key相应的entry,被编辑的Entry不能被remove
 722      * @return true if an entry was removed.
 723      */
 724     public synchronized boolean remove(String key) throws IOException {
 725         checkNotClosed();//检查cache是否已经关闭
 726         validateKey(key);//验证key格式的正确性
 727         Entry entry = lruEntries.get(key);
 728         if (entry == null || entry.currentEditor != null) {
 729             return false;
 730         }
 731 
 732 
 733         for (int i = 0; i < valueCount; i++) {
 734             File file = entry.getCleanFile(i);
 735             if (!file.delete()) {
 736                 throw new IOException("failed to delete " + file);
 737             }
 738             size -= entry.lengths[i];
 739             entry.lengths[i] = 0;
 740         }
 741 
 742 
 743         redundantOpCount++;
 744         journalWriter.append(REMOVE + ' ' + key + '\n');
 745         lruEntries.remove(key);
 746 
 747 
 748         if (journalRebuildRequired()) {
 749             executorService.submit(cleanupCallable);
 750         }
 751 
 752 
 753         return true;
 754     }
 755 
 756 
 757     /**
 758      * Returns true if this cache has been closed.
 759      * 判断cache是否已经关闭
 760      */
 761     public boolean isClosed() {
 762         return journalWriter == null;
 763     }
 764 
 765 
 766     //检查cache是否已经关闭
 767     private void checkNotClosed() {
 768         if (journalWriter == null) {
 769             throw new IllegalStateException("cache is closed");
 770         }
 771     }
 772 
 773 
 774     /**
 775      * Force buffered operations to the filesystem.
 776      */
 777     public synchronized void flush() throws IOException {
 778         checkNotClosed();//检查cache是否关闭
 779         trimToSize();//满足最大空间limit
 780         journalWriter.flush();
 781     }
 782 
 783 
 784     /**
 785      * Closes this cache. Stored values will remain on the filesystem.
 786      * 关闭cache。
 787      */
 788     public synchronized void close() throws IOException {
 789         if (journalWriter == null) {
 790             return; // already closed
 791         }
 792         for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {
 793             if (entry.currentEditor != null) {
 794                 entry.currentEditor.abort();
 795             }
 796         }
 797         trimToSize();
 798         journalWriter.close();
 799         journalWriter = null;
 800     }
 801 
 802 
 803     //回收删除某些entry到空间大小满足maxsize
 804     private void trimToSize() throws IOException {
 805         while (size > maxSize) {
 806 //            Map.Entry<String, Entry> toEvict = lruEntries.eldest();
 807             final Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
 808             remove(toEvict.getKey());
 809         }
 810     }
 811 
 812 
 813     /**
 814      * Closes the cache and deletes all of its stored values. This will delete
 815      * all files in the cache directory including files that weren't created by
 816      * the cache.
 817      * 关闭删除cache
 818      */
 819     public void delete() throws IOException {
 820         close();
 821         deleteContents(directory);
 822     }
 823 
 824 
 825     //验证key格式的正确性
 826     private void validateKey(String key) {
 827         if (key.contains(" ") || key.contains("\n") || key.contains("\r")) {
 828             throw new IllegalArgumentException(
 829                     "keys must not contain spaces or newlines: \"" + key + "\"");
 830         }
 831     }
 832 
 833 
 834     //字符串形式读出输入流的内容
 835     private static String inputStreamToString(InputStream in) throws IOException {
 836         return readFully(new InputStreamReader(in, UTF_8));
 837     }
 838 
 839 
 840     /**
 841      * A snapshot of the values for an entry.
 842      * entry的快照
 843      */
 844     public final class Snapshot implements Closeable {
 845         private final String key;//key
 846         private final long sequenceNumber;//序列号(同文件名称)
 847         private final InputStream[] ins;//两个修改的文件输入流
 848 
 849 
 850         private Snapshot(String key, long sequenceNumber, InputStream[] ins) {
 851             this.key = key;
 852             this.sequenceNumber = sequenceNumber;
 853             this.ins = ins;
 854         }
 855 
 856 
 857         /**
 858          * Returns an editor for this snapshot's entry, or null if either the
 859          * entry has changed since this snapshot was created or if another edit
 860          * is in progress.
 861          * 返回entry快照的editor,如果entry已经更新了或者另一个edit正在处理过程中返回null。
 862          */
 863         public Editor edit() throws IOException {
 864             return DiskLruCache.this.edit(key, sequenceNumber);
 865         }
 866 
 867 
 868         /**
 869          * Returns the unbuffered stream with the value for {@code index}.
 870          */
 871         public InputStream getInputStream(int index) {
 872             return ins[index];
 873         }
 874 
 875 
 876         /**
 877          * Returns the string value for {@code index}.
 878          */
 879         public String getString(int index) throws IOException {
 880             return inputStreamToString(getInputStream(index));
 881         }
 882 
 883 
 884         @Override public void close() {
 885             for (InputStream in : ins) {
 886                 closeQuietly(in);
 887             }
 888         }
 889     }
 890 
 891 
 892     /**
 893      * Edits the values for an entry.
 894      * entry编辑器
 895      */
 896     public final class Editor {
 897         private final Entry entry;
 898         private boolean hasErrors;
 899 
 900 
 901         private Editor(Entry entry) {
 902             this.entry = entry;
 903         }
 904 
 905 
 906         /**
 907          * Returns an unbuffered input stream to read the last committed value,
 908          * or null if no value has been committed.
 909          * 返回一个最后提交的entry的不缓存输入流,如果没有值被提交过返回null
 910          */
 911         public InputStream newInputStream(int index) throws IOException {
 912             synchronized (DiskLruCache.this) {
 913                 if (entry.currentEditor != this) {
 914                     throw new IllegalStateException();
 915                 }
 916                 if (!entry.readable) {
 917                     return null;
 918                 }
 919                 return new FileInputStream(entry.getCleanFile(index));
 920             }
 921         }
 922 
 923 
 924         /**
 925          * Returns the last committed value as a string, or null if no value
 926          * has been committed.
 927          * 返回最后提交的entry的文件内容,字符串形式
 928          */
 929         public String getString(int index) throws IOException {
 930             InputStream in = newInputStream(index);
 931             return in != null ? inputStreamToString(in) : null;
 932         }
 933 
 934 
 935         /**
 936          * Returns a new unbuffered output stream to write the value at
 937          * {@code index}. If the underlying output stream encounters errors
 938          * when writing to the filesystem, this edit will be aborted when
 939          * {@link #commit} is called. The returned output stream does not throw
 940          * IOExceptions.
 941          * 返回一个新的无缓冲的输出流,写文件时如果潜在的输出流存在错误,这个edit将被废弃。
 942          */
 943         public OutputStream newOutputStream(int index) throws IOException {
 944             synchronized (DiskLruCache.this) {
 945                 if (entry.currentEditor != this) {
 946                     throw new IllegalStateException();
 947                 }
 948                 return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
 949             }
 950         }
 951 
 952 
 953         /**
 954          * Sets the value at {@code index} to {@code value}.
 955          * 设置entry的value的文件的内容
 956          */
 957         public void set(int index, String value) throws IOException {
 958             Writer writer = null;
 959             try {
 960                 writer = new OutputStreamWriter(newOutputStream(index), UTF_8);
 961                 writer.write(value);
 962             } finally {
 963                 closeQuietly(writer);
 964             }
 965         }
 966 
 967 
 968         /**
 969          * Commits this edit so it is visible to readers.  This releases the
 970          * edit lock so another edit may be started on the same key.
 971          * commit提交编辑的结果,释放edit锁然后其它edit可以启动
 972          */
 973         public void commit() throws IOException {
 974             if (hasErrors) {
 975                 completeEdit(this, false);
 976                 remove(entry.key); // the previous entry is stale
 977             } else {
 978                 completeEdit(this, true);
 979             }
 980         }
 981 
 982 
 983         /**
 984          * Aborts this edit. This releases the edit lock so another edit may be
 985          * started on the same key.
 986          * 废弃edit,释放edit锁然后其它edit可以启动
 987          */
 988         public void abort() throws IOException {
 989             completeEdit(this, false);
 990         }
 991 
 992 
 993         //包装的输出流类
 994         private class FaultHidingOutputStream extends FilterOutputStream {
 995             private FaultHidingOutputStream(OutputStream out) {
 996                 super(out);
 997             }
 998 
 999 
1000             @Override public void write(int oneByte) {
1001                 try {
1002                     out.write(oneByte);
1003                 } catch (IOException e) {
1004                     hasErrors = true;
1005                 }
1006             }
1007 
1008 
1009             @Override public void write(byte[] buffer, int offset, int length) {
1010                 try {
1011                     out.write(buffer, offset, length);
1012                 } catch (IOException e) {
1013                     hasErrors = true;
1014                 }
1015             }
1016 
1017 
1018             @Override public void close() {
1019                 try {
1020                     out.close();
1021                 } catch (IOException e) {
1022                     hasErrors = true;
1023                 }
1024             }
1025 
1026 
1027             @Override public void flush() {
1028                 try {
1029                     out.flush();
1030                 } catch (IOException e) {
1031                     hasErrors = true;
1032                 }
1033             }
1034         }
1035     }
1036 
1037 
1038     /*
1039      * Entry 最终类
1040      */
1041     private final class Entry {
1042         private final String key;
1043 
1044 
1045         /** Lengths of this entry's files. */
1046         private final long[] lengths;//每一个cache文件的长度
1047 
1048 
1049         /** True if this entry has ever been published */
1050         private boolean readable;
1051 
1052 
1053         /** The ongoing edit or null if this entry is not being edited. */
1054         private Editor currentEditor;
1055 
1056 
1057         /** The sequence number of the most recently committed edit to this entry. */
1058         private long sequenceNumber;
1059 
1060 
1061         private Entry(String key) {
1062             this.key = key;
1063             this.lengths = new long[valueCount];
1064         }
1065 
1066 
1067         public String getLengths() throws IOException {
1068             StringBuilder result = new StringBuilder();
1069             for (long size : lengths) {
1070                 result.append(' ').append(size);
1071             }
1072             return result.toString();
1073         }
1074 
1075 
1076         /**
1077          * Set lengths using decimal numbers like "10123".
1078          * 设置每一个cache文件的长度(即lengths[i]的长度)
1079          */
1080         private void setLengths(String[] strings) throws IOException {
1081             if (strings.length != valueCount) {
1082                 throw invalidLengths(strings);
1083             }
1084 
1085 
1086             try {
1087                 for (int i = 0; i < strings.length; i++) {
1088                     lengths[i] = Long.parseLong(strings[i]);
1089                 }
1090             } catch (NumberFormatException e) {
1091                 throw invalidLengths(strings);
1092             }
1093         }
1094 
1095 
1096         private IOException invalidLengths(String[] strings) throws IOException {
1097             throw new IOException("unexpected journal line: " + Arrays.toString(strings));
1098         }
1099 
1100 
1101         public File getCleanFile(int i) {
1102             return new File(directory, key + "." + i);
1103         }
1104 
1105 
1106         public File getDirtyFile(int i) {
1107             return new File(directory, key + "." + i + ".tmp");
1108         }
1109     }
1110 }

转自http://blog.csdn.net/linghu_java/article/details/8600053

转载于:https://www.cnblogs.com/XP-Lee/p/3410062.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值