DiskLurCache 源碼總結(jié)

DiskLurCache

使用教程

源碼解析

使用

打開緩存

  1. 打開緩存函數(shù)

    public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)  
    

    ? open()方法接收四個(gè)參數(shù)怪瓶,第一個(gè)參數(shù)指定的是數(shù)據(jù)的緩存地址,第二個(gè)參數(shù)指定當(dāng)前應(yīng)用程序的版本號(hào),第三個(gè)參數(shù)指定同一個(gè)key可以對(duì)應(yīng)多少個(gè)緩存文件堤尾,基本都是傳1语御,第四個(gè)參數(shù)指定最多可以緩存多少字節(jié)的數(shù)據(jù)。

  2. 實(shí)際調(diào)用

    public File getDiskCacheDir(Context context, String uniqueName) {  
        String cachePath;  
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())  
            || !Environment.isExternalStorageRemovable()) {  
            cachePath = context.getExternalCacheDir().getPath();  
        } else {  
            cachePath = context.getCacheDir().getPath();  
        }  
        return new File(cachePath + File.separator + uniqueName);  
    }  
    
    public int getAppVersion(Context context) {  
        try {  
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);  
            return info.versionCode;  
        } catch (NameNotFoundException e) {  
            e.printStackTrace();  
        }  
        return 1;  
    }  
    
    //調(diào)用open 函數(shù), 這里的 open 函數(shù), 當(dāng)版本號(hào)改變后, 
    //appVersionString, valueCountString 改變的時(shí)候, 會(huì)直接 報(bào) IO異常
    DiskLruCache mDiskLruCache = null;  
    try {  
        File cacheDir = getDiskCacheDir(context, "bitmap");  
        if (!cacheDir.exists()) {  
            cacheDir.mkdirs();  
        }  
        mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
    
//private void readJournal() throws IOException  函數(shù).

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 + "]");
}

寫入緩存

  1. 下載一張圖片

    //調(diào)用 URL 系在一張圖片, 并寫入 outputStream 中.
    private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {  
        HttpURLConnection urlConnection = null;  
        BufferedOutputStream out = null;  
        BufferedInputStream in = null;  
        try {  
            final URL url = new URL(urlString);  
            urlConnection = (HttpURLConnection) url.openConnection();  
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);  
            out = new BufferedOutputStream(outputStream, 8 * 1024);  
            int b;  
            while ((b = in.read()) != -1) {  
                out.write(b);  
            }  
            return true;  
        } catch (final IOException e) {  
            e.printStackTrace();  
        } finally {  
            if (urlConnection != null) {  
                urlConnection.disconnect();  
            }  
            try {  
                if (out != null) {  
                    out.close();  
                }  
                if (in != null) {  
                    in.close();  
                }  
            } catch (final IOException e) {  
                e.printStackTrace();  
            }  
        }  
        return false;  
    }  
    

    ?

  2. 根據(jù) URL 生成 MD5值, 唯一標(biāo)識(shí), 當(dāng)作內(nèi)部的 LURCache List的鍵

    public String hashKeyForDisk(String key) {  
        String cacheKey;  
        try {  
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");  
            mDigest.update(key.getBytes());  
            cacheKey = bytesToHexString(mDigest.digest());  
        } catch (NoSuchAlgorithmException e) {  
            cacheKey = String.valueOf(key.hashCode());  
        }  
        return cacheKey;  
    }  
    
    private String bytesToHexString(byte[] bytes) {  
        StringBuilder sb = new StringBuilder();  
        for (int i = 0; i < bytes.length; i++) {  
            String hex = Integer.toHexString(0xFF & bytes[i]);  
            if (hex.length() == 1) {  
                sb.append('0');  
            }  
            sb.append(hex);  
        }  
        return sb.toString();  
    }
    
  3. 調(diào)用 edit(key) 獲取 Editor 對(duì)象

    String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
    String key = hashKeyForDisk(imageUrl);
    DiskLruCache.Editor editor = mDiskLruCache.edit(key); 
    
    //這里的 editor 里面可以生成一個(gè) 關(guān)于 dirty 文件的 output 對(duì)象, 并重寫了
    //輸出流的 write()函數(shù), 一旦寫操作報(bào) IO 異常, 會(huì)將 Entry 中的 hasError 置為 true,
    //在 commit 的時(shí)候會(huì)調(diào)用 commitEdit(false), 將 dirty 刪除掉, 
    //理想情況下是成功的, 那么會(huì)將文件保存為 clean 文件, 并記錄一行  DIRTY操作,
    
    //下載圖片使用的 outputStream 是editor 的, 也就是 指向 dirty 的 outputStream. 
    //在調(diào)用 commit/abort 函數(shù)時(shí),會(huì)將dirty文件轉(zhuǎn)換為clean文件,或者刪除掉.
    new Thread(new Runnable() {  
        @Override  
        public void run() {  
            try {  
                String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
                String key = hashKeyForDisk(imageUrl);  
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);  
                if (editor != null) {  
                    OutputStream outputStream = editor.newOutputStream(0);  
                    if (downloadUrlToStream(imageUrl, outputStream)) {  
                        editor.commit();  
                    } else {  
                        editor.abort();  
                    }  
                }
                //檢查當(dāng)前 存儲(chǔ)的size 是否大于設(shè)置的maxSize, 
                //大于, 將刪除 LUR 中原本不常用的 文件, 直到 size < maxSize.
                mDiskLruCache.flush();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }).start();  
    

讀取緩存

  1. 調(diào)用函數(shù)

    public synchronized Snapshot get(String key) throws IOException  
    
  2. 根據(jù) URL 生成的KEY 去獲取對(duì)應(yīng)的文件, 獲取 SnapShot 對(duì)象.

    String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
    String key = hashKeyForDisk(imageUrl);  
    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
    
  3. 調(diào)用 SnapShot 對(duì)象里面輸入流

    輸入流 指向的是 KEY 對(duì)應(yīng)的 CLEAN 文件 的 InputStrean, 并且在 SnapSnot 中保存的是一個(gè) inputStream 數(shù)組, 數(shù)組的長(zhǎng)度是 valueCount(一個(gè)KEY 對(duì)應(yīng)幾個(gè)文件) 的大小,

    每個(gè) inputStream[] 保存的是對(duì)應(yīng)的下標(biāo) 文件, 具體的 獲取文件的函數(shù)在 Entry 中, 調(diào)用 getCleanFile(index) 函數(shù)獲取.

    ? 這里調(diào)用getInputStream(0), 將 inputStream 轉(zhuǎn)換為 Bitmap 并顯示出來.

    try {  
        String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
        String key = hashKeyForDisk(imageUrl);  
        DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
        if (snapShot != null) {  
            InputStream is = snapShot.getInputStream(0);  
            Bitmap bitmap = BitmapFactory.decodeStream(is);  
            mImage.setImageBitmap(bitmap);  
        }  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
    

移除緩存

mDiskLruCache.remove(key); 調(diào)用 remove 函數(shù), 關(guān)鍵判斷為 key 值來刪除

try {  
    String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";    
    String key = hashKeyForDisk(imageUrl);    
    mDiskLruCache.remove(key);  
} catch (IOException e) {  
    e.printStackTrace();  
}  

將會(huì)在 journal 文件中寫入一行 REMOVE 操作, 并將 redundantOpCount++

源碼解析

概述

  • DiskLurCache 涉及到一個(gè) journal 的文件, 這個(gè)文件保存 CLEAN, DIRTY, REMOVE, READ 操作

  • 初始化一個(gè) DiskLurCache 對(duì)象, 需要調(diào)用 open 函數(shù)

    DiskLruCache.open(directory, appVersion, 
                      valueCount, maxSize) ;
    
  • 關(guān)于寫操作

    String key = generateKey(url);  
    DiskLruCache.Editor editor = mDiskLruCache.edit(key); 
    OuputStream os = editor.newOutputStream(0);
    
    os.write(...)
    os.falsh();
    
    //提交寫操作, 將之前寫的 temp 文件保存為 clean 文件, 
    //當(dāng)之前寫操作出現(xiàn)錯(cuò)誤的時(shí)候, 會(huì)將文件刪除, 并將 KEY 從 lur 中刪除掉.
    editor.commit();
    
  • 關(guān)于讀操作

    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
    if (snapShot != null) {  
        InputStream is = snapShot.getInputStream(0);  
    }
    
    Bitmap bitmap = BitmapFactory.decodeStream(is);
    imageView.setBitmap(bitmap);
    
    //關(guān)閉所有的 inputStream.
    snapShot.close();
    

journal 文件

journal文件你打開以后呢,是這個(gè)格式切黔;

libcore.io.DiskLruCache
1
1
1

DIRTY c3bac86f2e7a291a1a200b853835b664
CLEAN c3bac86f2e7a291a1a200b853835b664 4698
READ c3bac86f2e7a291a1a200b853835b664
DIRTY c59f9eec4b616dc6682c7fa8bd1e061f
CLEAN c59f9eec4b616dc6682c7fa8bd1e061f 4698
READ c59f9eec4b616dc6682c7fa8bd1e061f
DIRTY be8bdac81c12a08e15988555d85dfd2b
CLEAN be8bdac81c12a08e15988555d85dfd2b 99
READ be8bdac81c12a08e15988555d85dfd2b
DIRTY 536788f4dbdffeecfbb8f350a941eea3
REMOVE 536788f4dbdffeecfbb8f350a941eea3 

首先看前五行:ok板乙,以上5行可以稱為該文件的文件頭是偷,DiskLruCache初始化的時(shí)候,如果該文件存在需要校驗(yàn)該文件頭募逞。

DiskLruCache初始化的時(shí)候蛋铆,如果該文件存在需要校驗(yàn)該文件頭。

  • 第一行固定字符串libcore.io.DiskLruCache
  • 第二行DiskLruCache的版本號(hào)放接,源碼中為常量1
  • 第三行為你的app的版本號(hào)刺啦,當(dāng)然這個(gè)是你自己傳入指定的
  • 第四行指每個(gè)key對(duì)應(yīng)幾個(gè)文件,一般為1
  • 第五行纠脾,空行

操作記錄:

  • DIRTY 表示一個(gè)entry正在被寫入(其實(shí)就是把文件的OutputStream交給你了)玛瘸。那么寫入分兩種情況,如果成功會(huì)緊接著寫入一行CLEAN的記錄苟蹈;如果失敗糊渊,會(huì)增加一行REMOVE記錄。
  • REMOVE除了上述的情況呢慧脱,當(dāng)你自己手動(dòng)調(diào)用remove(key)方法的時(shí)候也會(huì)寫入一條REMOVE記錄渺绒。
  • READ就是說明有一次讀取的記錄。
  • 每個(gè)CLEAN的后面還記錄了文件的長(zhǎng)度菱鸥,注意可能會(huì)一個(gè)key對(duì)應(yīng)多個(gè)文件宗兼,那么就會(huì)有多個(gè)數(shù)字(參照文件頭第四行)。

DiskLruCache#open

  1. open 函數(shù)

      /**
       * 打開緩存在文件夾中, 如果不存在就創(chuàng)建.
       * 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. 每個(gè)緩存條目的值數(shù)量. 每個(gè) KEY 對(duì)應(yīng)的文件
       * @param maxSize the maximum number of bytes this cache should use to store  用于存儲(chǔ)的最大字節(jié)數(shù).
       * @throws IOException if reading or writing the cache directory fails   當(dāng)寫文件和度文件失敗, 會(huì)拋出異常.
       */
      //創(chuàng)建 DiskLruCache 對(duì)象, 并初始化文件存放的地址.
      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");
        }
    
        //查找 bkp 文件是否存在, 不存在
        // 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.
          //即存在 bkp 文件又存在 journal 文件,  刪除backup 文件,
          //存在 bkp 文件,但是不存在 journal文件, 將文件重命名.
          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);
        //根據(jù)文件夾信息, 創(chuàng)建對(duì)應(yīng)的源信息, 和backup , 和臨時(shí)操作的文件.
        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;
      }
    
    
  2. 重新創(chuàng)建 journal 文件 (1. 文件不存在, 2. 文件存在, 但是多余的操作超過 2000, 為了保證 journal 文件的大小, 會(huì)重新生成文件.)

     /**
       * 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();
        }
    
        //新將數(shù)據(jù)寫到  tmp 文件中.
        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");
    
          //重新寫文件, REMOVE, READ 操作會(huì)被干光, 重新寫文件.
          for (Entry entry : lruEntries.values()) {
            //判斷entry 是否是臟數(shù)據(jù)的存在.
            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()) {
          //在清除 REMOVE, READ 操作的時(shí)候, 如果 之前存在  journalFile, 那么將文件保存為 Backup 文件, 并將源文件刪除.
          renameTo(journalFile, journalFileBackup, true);
        }
        //將 tmp 文件重新保存為 journalFile 文件, 但是不刪除 tmp 文件.
        renameTo(journalFileTmp, journalFile, false);
        //在轉(zhuǎn)換成功后, 將 備份文件也刪除掉, 在 DiskLurCache 的每關(guān)于文件的操作都會(huì)將 IO 異常拋出去,
        //這里就是當(dāng) renameTo() 這個(gè)函數(shù)被拋出了 IO 異常的時(shí)候備份文件不會(huì)被刪除掉.
        journalFileBackup.delete();
    
        journalWriter = new BufferedWriter(
            new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));
      }
    
    
  3. 初始化的時(shí)候, 文件存在, 讀取文件行, 并保證 lur 中保存的記錄是只有 CLEAN, 而沒有 REMOVE/ DIRTY 操作的 KEY - ENTRY

    
      //只會(huì)在初始化的時(shí)候被調(diào)用 open() 函數(shù)的時(shí)候才會(huì)被調(diào)用.
    
        /**
         * 1. 通過文件頭來檢測(cè)是否是 journal 文件, 如果不是, 直接報(bào)IO 異常,
         * 2. 對(duì)文件進(jìn)行while 循環(huán), 一直跑到捕獲文件尾異常,
         *       2.1. while 循環(huán)中會(huì)將 標(biāo)簽為 CLEAN 標(biāo)志的標(biāo)簽的KEY 添加到 LUR 數(shù)組中去,
         *            但是當(dāng)在輪詢中碰到 Remove 的操作標(biāo)簽, 會(huì)將 對(duì)應(yīng)的 KEY 從原本的 LUR 數(shù)組中移除,
         *
         *       2.3. 判斷3個(gè)狀態(tài), REMOVE, CLEAN, DIRTY,
         *             REMOVE: 會(huì)刪除在 LUR 中的 KEY 值.
         *             CLEAN:  生成一個(gè) Entry(不管是不是空的),
         *                          設(shè)置 currentEditor = null,
         *                          設(shè)置 readable = true (可讀)
         *                          設(shè)置 lengths, 也是通過空格來區(qū)分的.
         *
         *             DIRTY: 臟數(shù)據(jù), 如果文件是臟數(shù)據(jù)(正在操作)時(shí). 分配一個(gè)新的  Editor(關(guān)于 entry的 Editor(文件流))
         *         REMOVE, CLEAR, DIRTY, READ, 和KEY 之間都有空格, 他們之間的判斷第一個(gè)是名稱, 第二個(gè)是空格的數(shù)量.
         *
         *       2.4. 統(tǒng)計(jì)當(dāng)前文件中多余的操作次數(shù):
         *            文本的行數(shù) - LUR.size() = 多余操作次數(shù).
         *
         *       2.5. 判斷文件的讀寫是否是異常停止, 文件未讀到末尾, 則調(diào)用 reBuildJournal() 函數(shù). 重寫生成 journal 文件.
         *
         *       2.6. journalWriter 初始化 journalWriter, 寫字段到 journal文件中的 輸出流.
         *
         * @throws IOException
         */
    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) {
                    //捕獲 crash 來跳出循環(huán).
                    break;
                }
            }
    
            //多余的操作次數(shù).
            redundantOpCount = lineCount - lruEntries.size();
    
            // If we ended on a truncated line, rebuild the journal before appending to it.
            //函數(shù)執(zhí)行錯(cuò)誤, 未結(jié)束, 但是報(bào)了 EOFException 錯(cuò)誤.
            if (reader.hasUnterminatedLine()) {
                rebuildJournal();
            } else {
                //初始化 write.
                journalWriter = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(journalFile, true), Util.US_ASCII));
            }
        } finally {
            //關(guān)閉 reader 流.
            Util.closeQuietly(reader);
        }
    }
    
    private void readJournalLine(String line) throws IOException {
        //切割字符串, 準(zhǔn)備判斷關(guān)于 KEY 值的狀態(tài), 是否需要被刪除掉.
        int firstSpace = line.indexOf(' ');
        if (firstSpace == -1) {
            throw new IOException("unexpected journal line: " + line);
        }
    
        int keyBegin = firstSpace + 1;
        int secondSpace = line.indexOf(' ', keyBegin); // 查找第二個(gè)空格.
        final String key;
        if (secondSpace == -1) {
            key = line.substring(keyBegin);
            //判斷狀態(tài)是否被標(biāo)志位 REMOVE. 是的話, 將會(huì)被移除.
            if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {
                //lruEntries 是沒有數(shù)據(jù)的. 刪除對(duì)應(yīng)的標(biāo)識(shí), 也就是 一個(gè) key(一個(gè)文件) 的可能被多次操作.
                lruEntries.remove(key);
                return;
            }
        } else {
            key = line.substring(keyBegin, secondSpace);
        }
    
        //將key 值和 Entry() 保存在 lruEntries 中.
        Entry entry = lruEntries.get(key);
        if (entry == null) {
            entry = new Entry(key);
            lruEntries.put(key, entry);
        }
    
        if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) {
            //當(dāng)前的狀態(tài)為 Clean , 即將數(shù)據(jù)保存起來了, 或者洗衣個(gè)將會(huì)刪除數(shù)據(jù).
            //獲取key 之后的string, 使用空格分割, 分割出來的 lengths 即時(shí)對(duì)應(yīng)的 entry lengths 的值.
            String[] parts = line.substring(secondSpace + 1).split(" ");
            entry.readable = true;
            entry.currentEditor = null;
            entry.setLengths(parts);  //設(shè)置文件的長(zhǎng)度.
        } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) {
            entry.currentEditor = new Editor(entry); //如果文件是臟數(shù)據(jù)(正在操作)時(shí). 分配一個(gè)新的  Editor(關(guān)于 entry的 Editer(文件流))
        } 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.
       *
       * 1. 計(jì)算已經(jīng)保存文件的長(zhǎng)度,(CLEAN 標(biāo)記的)
       * 2. 刪除 DIRTY 標(biāo)記的條目對(duì)應(yīng)的 文本文件和 設(shè)置 entry 為null.
       *    并將自己從原本的LUR數(shù)列中刪除掉, 擦除記錄.
       */
    private void processJournal() throws IOException {
        deleteIfExists(journalFileTmp);//刪除 臨時(shí)文件.
        for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
            Entry entry = i.next();
            //文件的操作點(diǎn)是  CLEAR. 也就是干凈的,
            if (entry.currentEditor == null) {
                //valueCount 是針對(duì)一個(gè) key 能存多上個(gè) value., 數(shù)據(jù)存在 Entry 里面.
                for (int t = 0; t < valueCount; t++) {
                    //增加文件的長(zhǎng)度, size.
                    size += entry.lengths[t];
                }
            } else {
                entry.currentEditor = null;
                //刪除對(duì)應(yīng)的 cleanFIle 和臟數(shù)據(jù), 只要key 值被標(biāo)記了 REMOVE / 臟數(shù)據(jù)操作的標(biāo)記, 那么之前就會(huì)有 CLEAN 操作
                // 這個(gè)地方會(huì)將原本的操作也刪除掉.
                for (int t = 0; t < valueCount; t++) {
                    deleteIfExists(entry.getCleanFile(t));
                    deleteIfExists(entry.getDirtyFile(t));
                }
                //從數(shù)組中刪除自己.
                i.remove();
            }
        }
    }
    
  4. open 總結(jié)

    經(jīng)過open以后氮采,journal文件肯定存在了殷绍;lruEntries里面肯定有值了;size存儲(chǔ)了當(dāng)前所有的實(shí)體占據(jù)的容量鹊漠;主到。

存入緩存

  1. 示例

    String key = generateKey(url);  
    DiskLruCache.Editor editor = mDiskLruCache.edit(key); 
    OuputStream os = editor.newOutputStream(0); 
    //...after op
    editor.commit()殖侵;
    
  2. 調(diào)用對(duì)外部提供的 edit 函數(shù), 獲取 Editor 對(duì)象

    /**
       * Returns an editor for the entry named {@code key}, or null if another
       * edit is in progress.
       *
       * 對(duì)外開發(fā) 獲取 Editor 對(duì)象的函數(shù), 根據(jù) KEY獲取 Editor 對(duì)象
       */
    public Editor edit(String key) throws IOException {
        return edit(key, ANY_SEQUENCE_NUMBER);
    }
    
    /**
         * 創(chuàng)建一個(gè)新的 Editor 對(duì)象, 將從 LUR 里面獲取的 Entry / 重新創(chuàng)建的 Entry 對(duì)象賦值到 Editor 上面去.
         * 并給 Entry 賦值  entry.currentEditor = editor.
         *
         *  在日志文件中寫入 DIRTY 操作日志.
         *
         *  注: 在初始化 Editor 之前,會(huì)先判斷  entry.currentEditor != null ,
         *      如果
         *
         * 1. 檢查當(dāng)前的 write 是否被close, 檢查key值的正常
         * 2. 創(chuàng)建一個(gè)新的 KEY / LUR 中獲取, 并將 editor.currentEntry 指向當(dāng)前的 entry.
         * 3. 記錄一行臟數(shù)據(jù)操作, 并返回 Editor對(duì)象,
         */
    private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
        checkNotClosed();
        validateKey(key);
        Entry entry = lruEntries.get(key);
        if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
                                                              || entry.sequenceNumber != expectedSequenceNumber)) {
            return null; // Snapshot is stale.
        }
        //當(dāng) entry 為 null 時(shí), 創(chuàng)建一個(gè)  entry 對(duì)象并保存到  LUR 里面去.
        if (entry == null) {
            entry = new Entry(key);
            lruEntries.put(key, entry);
            //當(dāng)前的 editor 正在被操作, 直接返回 null.
        } else if (entry.currentEditor != null) {
            return null; // Another edit is in progress.
        }
    
        //創(chuàng)建一個(gè)新的 Editor對(duì)象, 并設(shè)置 currentEditor 對(duì)象為之前的 Entry, 或者 lruEntries.get(key) 獲取的editor
        Editor editor = new Editor(entry);
        entry.currentEditor = editor;
    
        //設(shè)置臟當(dāng)前的KEY 為臟數(shù)據(jù)標(biāo)記.
        // Flush the journal before creating files to prevent file leaks.
        journalWriter.write(DIRTY + ' ' + key + '\n');
        journalWriter.flush();
        return editor;
    }
    
  3. Editor/ Entry 對(duì)象

     //空的 output , 對(duì)write 做的操作都不做任何事情.
      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;
        //對(duì)應(yīng)的 寫入者.
        private final boolean[] written;
        //FilterOutputStream 的任何流操作,出了異常,都會(huì)被標(biāo)記為 error true.
        private boolean hasErrors;
        //是否提交完成標(biāo)記
        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.
         *  inputStream 需要外部自己close.
         */
        public InputStream newInputStream(int index) throws IOException {
          synchronized (DiskLruCache.this) {
            if (entry.currentEditor != this) {
              throw new IllegalStateException();
            }
            //數(shù)據(jù)沒有被寫入過.
            if (!entry.readable) {
              return null;
            }
            //數(shù)據(jù)被寫入過, 直接返回  文件流 對(duì)象.
            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.
         */
        /**
         * index 指的是在用戶傳入的 一個(gè)key 對(duì)應(yīng)幾個(gè) 文件的下標(biāo),
         */
        public OutputStream newOutputStream(int index) throws IOException {
          if (index < 0 || index >= valueCount) {
            throw new IllegalArgumentException("Expected index " + index + " to "
                    + "be greater than 0 and less than the maximum value count "
                    + "of " + valueCount);
          }
          synchronized (DiskLruCache.this) {
            if (entry.currentEditor != this) {
              throw new IllegalStateException();
            }
            //當(dāng)對(duì)應(yīng)的  entry 對(duì)應(yīng)的 KEY 之前沒有被寫入, 那么 WRITTEN[i] 會(huì)被置為 true.
            if (!entry.readable) {
              written[index] = true;
            }
    
            //獲取 outPut 寫入文件的存放位置在 臟文件中,(.tmp), 其中是根據(jù) index 來命名的.
            File dirtyFile = entry.getDirtyFile(index);
            FileOutputStream outputStream;
            //兩次打開文件, 第一次可以判斷文件夾不存在的時(shí)候, 會(huì)創(chuàng)建文件夾,
            //然后, 再次打開文件,
            //如果還是報(bào)錯(cuò)誤了, 會(huì)返回一個(gè)對(duì) Write 無處理的的 output,
            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;
              }
            }
            //返回正常情況下的 OutPutStream.,
            //數(shù)據(jù)操作中的  OutputWriter 操作的數(shù)據(jù)會(huì)被直接寫入到臟文件中保存.
            return new FaultHidingOutputStream(outputStream);
          }
        }
    
        /** Sets the value at {@code index} to {@code value}. */
        public void set(int index, String value) throws IOException {
          //單獨(dú)創(chuàng)建一個(gè) Writer 對(duì)像, 將數(shù)據(jù)保存到指定文件中.
          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.
         *
         * 提交數(shù)據(jù)到硬盤上,當(dāng) hasErrors 不為 true 時(shí),
         * 會(huì)調(diào)用  completeEdit(this, true) 函數(shù)將 數(shù)據(jù)寫入到指定位置,
         *
         *  將tmp 文件寫成 clean 文件.
         */
        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.
         *
         * 將temp 文件刪除掉.
         */
        public void abort() throws IOException {
          completeEdit(this, false);
        }
    
        //在提交中, 想要中斷提交.
        public void abortUnlessCommitted() {
          if (!committed) {
            try {
              abort();
            } catch (IOException ignored) {
            }
          }
        }
    
        //輸出流的寫函數(shù)都被 try/catch, 一旦報(bào)錯(cuò),就在commit 的時(shí)候, 將 DIRTY 文件刪除掉 
        //并將對(duì)應(yīng)的 KEY 從對(duì)應(yīng)的 LUR 數(shù)組中移除.
        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 {
        //entry 對(duì)應(yīng)的KEY值, 唯一標(biāo)識(shí)符
        private final String key;
    
        /** Lengths of this entry's files.
         *  一個(gè) KEY 對(duì)應(yīng)的文件個(gè)數(shù), 使用lengths 來表示.
         * */
        private final long[] lengths;
    
        /** True if this entry has ever been published.
         * 設(shè)置當(dāng)前文件是否 可讀, 在被成功寫入時(shí) "CLEAR" 會(huì)伴隨這對(duì)應(yīng)的 entry.readable 被標(biāo)記為true.
         * readJournalLine() 時(shí), 當(dāng)前的標(biāo)志為 CLEAN時(shí), 會(huì)被標(biāo)記為可讀.
         * */
        private boolean readable;
    
        /** The ongoing edit or null if this entry is not being edited.
         *  當(dāng)前編輯, 當(dāng)狀態(tài)為  Clear 是, 這個(gè)對(duì)象為 null,
         *  當(dāng)為臟數(shù)據(jù)時(shí),  currentEditor 不為空.
         * */
        private Editor currentEditor;
    
        /** The sequence number of the most recently committed edit to this entry. */
        //最近提交的序列號(hào), 主要的用處是 Snapshot 對(duì)象獲取快照, 但是原本的文件被改動(dòng)了, 就直接return null.
        private long sequenceNumber;
    
        //輸入一個(gè)新 KEY, 并創(chuàng)建一個(gè) 長(zhǎng)度為 valueCount 的 int 數(shù)組 lengths
        private Entry(String key) {
          this.key = key;
          this.lengths = new long[valueCount];
        }
    
        //根據(jù) lengths 的個(gè)數(shù),返回  String, 使用分隔符 ' ' 分割.
        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".
         *  這里的 valueCount 是指的起初 open() 設(shè)置進(jìn)來的一個(gè) Key 對(duì)應(yīng)幾個(gè)文件,
         *  其中每個(gè)文件以 0,1,2,3,... 來區(qū)分.
         *
         *  將對(duì)應(yīng)文件的長(zhǎng)度設(shè)置進(jìn)來.
         * */
        private void setLengths(String[] strings) throws IOException {
          if (strings.length != valueCount) {
            throw invalidLengths(strings);
          }
    
          //保存 lengths 到 Entry 的 length 上面,
          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));
        }
    
        //對(duì)應(yīng)的文件使用 test.0, test.1 ... 來存放
        public File getCleanFile(int i) {
          return new File(directory, key + "." + i);
        }
    
        //對(duì)應(yīng)的文件使用 test.0, test.1 ... 來存放
        public File getDirtyFile(int i) {
          return new File(directory, key + "." + i + ".tmp");
        }
      }
    
  4. 對(duì) outPutSrream 操作的關(guān)鍵操作, 調(diào)用 edit的commit 函數(shù)才會(huì)被調(diào)用, 在editor 參數(shù)被調(diào)用的 時(shí)候, 返回的 OutputStream 指向的是 DIRTY 文件, 不存在,創(chuàng)建.

    
        /**
         * 1. 保存/刪除 緩存文件 --> success. success = true 時(shí), 將臟數(shù)據(jù)文件 寫成對(duì)應(yīng)的 clean 文件, 并刪除原本的臟數(shù)據(jù)文件
         *                                  success = false時(shí), 將臟數(shù)據(jù)直接刪除. 不保存成 clean文件,
         * 2. 多余的操作++ (DIRTY).
         *
         * 3. 檢查是否關(guān)于  valueCount 長(zhǎng)度的 output 都同事被操作聊(第一次的時(shí)候) , 不然直接報(bào)錯(cuò)了.  readable(只有已經(jīng)被寫入的文件才會(huì)有這個(gè)標(biāo)記,)
         *
         * 4.
         *
         */
      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.
        //第一次寫入文件,必須每個(gè)對(duì)應(yīng)的index 都有值, 不然這個(gè)地方會(huì) 報(bào)錯(cuò),
        // 當(dāng) valueCount = 1 的時(shí)候, 這個(gè)比較好理解,
        //當(dāng) valueCount = 3 的時(shí)候, 每次提交新的 KEY 的時(shí)候, 對(duì)應(yīng)的 index 必須在 commit() 函數(shù)之前先調(diào)用,
        //不然在這個(gè)位置會(huì)報(bào)錯(cuò)誤,
        if (success && !entry.readable) {
          for (int i = 0; i < valueCount; i++) {
            //written 這個(gè)數(shù)組是在 readable 為 false 的時(shí)候才會(huì)被置為 true.
            //也就是第一次使用對(duì)應(yīng)KEY 時(shí)才會(huì)被置為TRUE.
            if (!editor.written[i]) {
              editor.abort();
                                             //新創(chuàng)建的條目沒有為索引創(chuàng)建價(jià)值
              throw new IllegalStateException("Newly created entry didn't create value for index " + i);
            }
    
            //獲取使用 newOutPutStream 操作的那個(gè)臟文件是否存在,
            //不存在時(shí), 直接 return , 并記那個(gè) success 置為false.
            if (!entry.getDirtyFile(i).exists()) {
              //刪除對(duì)應(yīng)的臟文件, 并重新寫入對(duì)應(yīng)的  CLEAN / REMOVE 的記錄.
              editor.abort();
              return;
            }
          }
        }
    
        //將對(duì)應(yīng)的臟文件修改為CLEAN  文件.
        for (int i = 0; i < valueCount; i++) {
          File dirty = entry.getDirtyFile(i);
          if (success) {
            //對(duì)應(yīng)的 index 文件存在的時(shí)候,才會(huì)將文件 rename.
            if (dirty.exists()) {
              File clean = entry.getCleanFile(i);
              dirty.renameTo(clean);
    
              //重新設(shè)置 size 大小, 相對(duì)臟文件.
              long oldLength = entry.lengths[i];
              long newLength = clean.length();
              entry.lengths[i] = newLength;
              size = size - oldLength + newLength;
            }
          } else {
            //success = false 時(shí), 將刪除掉 臟數(shù)據(jù)文件.
            deleteIfExists(dirty);
          }
        }
    
        redundantOpCount++;
        entry.currentEditor = null;
        //當(dāng) readable 為true (之前被寫入過), 或者 success時(shí), 將重新寫入 SIZE.
        if (entry.readable | success) {
          entry.readable = true;
          //重新寫入 文件lengths 長(zhǎng)度, 當(dāng)對(duì)應(yīng)的文件沒有數(shù)據(jù)時(shí), 長(zhǎng)度為0.
          journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
          //當(dāng)重新寫入了 CLEAN 時(shí), entry 會(huì)被重新寫入 sequenceNumber.
          if (success) {
            entry.sequenceNumber = nextSequenceNumber++;
          }
        } else {
          //刪除 LUR 里面的 KEY , 并寫入 REMOVE 操作.
          lruEntries.remove(entry.key);
          journalWriter.write(REMOVE + ' ' + entry.key + '\n');
        }
        //關(guān)閉寫入操作
        journalWriter.flush();
    
        //重新計(jì)算大小.
        if (size > maxSize || journalRebuildRequired()) {
          executorService.submit(cleanupCallable);
        }
      }
    
  5. 保證文件大小限制大小操作

     //執(zhí)行的時(shí)機(jī):
      //1. 在使用 get() 函數(shù)獲取 Snapshot 對(duì)象的時(shí)候, 可能會(huì)觸發(fā)一次    if (journalRebuildRequired())
      //2. 重新設(shè)置 MaxSize  setMaxSize(maxSize)                     if( journalRebuildRequired() )
      //3. 調(diào)用數(shù)據(jù)提交時(shí): completeEdit();                            if (size > maxSize || journalRebuildRequired())
      //4. 調(diào)用 remove(key) 函數(shù)時(shí).                                   if( journalRebuildRequired() )
      // journalRebuildRequired() 函數(shù), 判斷的是 :
      //  redundantOpCount 參數(shù): 多余的操作次數(shù): 1. 在調(diào)用 readJournal() 的時(shí)候會(huì)只有 clean() 并沒有被移除的條目會(huì)被添加到 LUR 中,
      // 然后其他的多余的操作還有 remove() 會(huì)寫入 REMOVE 字段,
      // completeEdit() 的時(shí)候會(huì)寫入 DIRTY 字段,
      // get() 的時(shí)候會(huì)顯示 READ 參數(shù).
      private final Callable<Void> cleanupCallable = new Callable<Void>() {
        public Void call() throws Exception {
          synchronized (DiskLruCache.this) {
            if (journalWriter == null) {
              return null; // Closed.
            }
    
            //當(dāng) 當(dāng)前文件 SIZE 大于設(shè)置進(jìn)來的 maxSize 值, 會(huì)移除不常使用的文件,
            //保證保存的文件是 最常使用的, 并全部長(zhǎng)度加起來 < maxSize.
            //LUR 算法的優(yōu)勢(shì).
            trimToSize();
            if (journalRebuildRequired()) {
    
              rebuildJournal();
              redundantOpCount = 0;
            }
          }
          return null;
        }
      };
    
  6. 檢測(cè)文件大小操作

     //只有當(dāng) 多余操作大于 2000 并且多余操作大于等于 緩存數(shù)據(jù)的長(zhǎng)度.
      private boolean journalRebuildRequired() {
        final int redundantOpCompactThreshold = 2000;
        return redundantOpCount >= redundantOpCompactThreshold //
            && redundantOpCount >= lruEntries.size();
      }
    
  7. 檢查 SIZE 是否大于 maxSize, 大于的時(shí)候,會(huì)刪除不常用的 文件, 調(diào)用 remove 函數(shù), 刪除 KEY對(duì)應(yīng)的文件.

    調(diào)用時(shí)機(jī):

    • cleanupCallable() 被調(diào)用
    • close() 被調(diào)用時(shí)
    • flash() 被調(diào)用時(shí)
    /**
       *  借助 LUR 算法的幫助, 函數(shù) trimToSize() 會(huì)刪除最近不常使用的 key.
       *  http://blog.csdn.net/justloveyou_/article/details/71713781
       */
    private void trimToSize() throws IOException {
        while (size > maxSize) {
            Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
            remove(toEvict.getKey());
        }
    }
    

讀操作

  1. 示例

    try {  
        String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
        String key = hashKeyForDisk(imageUrl);  
        DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
        if (snapShot != null) {  
            InputStream is = snapShot.getInputStream(0);  
            Bitmap bitmap = BitmapFactory.decodeStream(is);  
            mImage.setImageBitmap(bitmap);  
        }  
    } catch (IOException e) {  
        e.printStackTrace();  
    } 
    
  2. get() 函數(shù)

 /**
   * 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.
   *
   * 1. 通過 KEY 拿到 readable 的 entry 對(duì)象, 可以被讀寫的 CLEAN 標(biāo)記的 文件
   * 2. 申請(qǐng)一個(gè) 長(zhǎng)度為 valueCount 的 InputStream 數(shù)據(jù), 并將對(duì)應(yīng)  entry 的 clean 文件賦值給 inputStream
   * 3. 為 KEY 寫入 READ 標(biāo)記, 多余的操作++
   * 4. 返回一個(gè) Snapshot對(duì)象.
   */
  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);
  }

其他操作

  1. remove(key)

     /**
       * 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.
       *
       * 1. 判斷當(dāng)前entry 的 currentEditor 是否為null, 為null 證明沒有在操作,
       *    在被操作, 直接返回 false.
       * 2. 根據(jù) valueCount 長(zhǎng)度, 循環(huán)減去 將要被刪除的文件長(zhǎng)度大小, 更新 size 大小,
       *     重置 entry lengths 全部為0.
       *
       * 3. 多余的操作++ , 從 LUR 中刪除 key, 和 檢查當(dāng)前多余操作是否過多,
       *       如果過多會(huì)被指向重寫生成 journal文件,刪除文件中 REMORE, READ 操作, 和已經(jīng)被刪除文件的
       *       KEY 操作
       */
      public synchronized boolean remove(String key) throws IOException {
        checkNotClosed();  //判斷 寫入流 是否為空,
        validateKey(key);
        Entry entry = lruEntries.get(key);
        //當(dāng)當(dāng)前文件還在被編輯, 或者當(dāng)前的 entry 不被保存在 LUR 里面, 會(huì)直接返回 false.
        if (entry == null || entry.currentEditor != null) {
          return false;
        }
        //刪除 對(duì)應(yīng)的cleanFile 文件, 一個(gè) key 對(duì)應(yīng)對(duì)個(gè)文件.
        for (int i = 0; i < valueCount; i++) {
          File file = entry.getCleanFile(i);
          if (file.exists() && !file.delete()) {
            throw new IOException("failed to delete " + file);
          }
          //實(shí)時(shí)改變 size 大小
          size -= entry.lengths[i];
          //修改entry 的長(zhǎng)度 length 大小
          entry.lengths[i] = 0;
        }
    
        redundantOpCount++;
        //寫入 remove 操作條例到文件中.
        journalWriter.append(REMOVE + ' ' + key + '\n');
        //刪除key.
        lruEntries.remove(key);
    
        //檢測(cè)多余的操作數(shù) > 2000 && > lur.size().
        if (journalRebuildRequired()) {
          //重新  rebuilder.
          executorService.submit(cleanupCallable);
        }
    
        return true;
      }
    
    
  2. public File getDirectory() : 返回當(dāng)前緩存數(shù)據(jù)的目錄

  3. public synchronized long getMaxSize() : 獲取設(shè)置的緩存的最大大小

  4. public synchronized long size() : //獲取當(dāng)前存儲(chǔ)的 占用 硬盤空間, byte.

  5. public synchronized boolean isClosed() : 判斷寫 日志文件的 journalWriter是否被 close() 掉了, 置為null

  6. public synchronized void flush() : 調(diào)用 trimToSize() 函數(shù), 和關(guān)閉 調(diào)用 journalWriter 的 flush() 函數(shù)

  7. public synchronized void close() : 關(guān)閉當(dāng)前寫 LOG 文件的 journalWriter , 并停止所有的 寫操作, 中斷

  8. public void delete() : 刪除傳入的 緩存目錄, 遞歸刪除全部的文件 .

Done.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市镰烧,隨后出現(xiàn)的幾起案子拢军,更是在濱河造成了極大的恐慌,老刑警劉巖怔鳖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茉唉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡结执,警方通過查閱死者的電腦和手機(jī)度陆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來献幔,“玉大人懂傀,你說我怎么就攤上這事±校” “怎么了蹬蚁?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)郑兴。 經(jīng)常有香客問我犀斋,道長(zhǎng),這世上最難降的妖魔是什么情连? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任叽粹,我火速辦了婚禮,結(jié)果婚禮上却舀,老公的妹妹穿的比我還像新娘虫几。我一直安慰自己,他們只是感情好挽拔,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布辆脸。 她就那樣靜靜地躺著,像睡著了一般篱昔。 火紅的嫁衣襯著肌膚如雪每强。 梳的紋絲不亂的頭發(fā)上始腾,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天州刽,我揣著相機(jī)與錄音,去河邊找鬼浪箭。 笑死穗椅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奶栖。 我是一名探鬼主播匹表,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼门坷,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了袍镀?” 一聲冷哼從身側(cè)響起默蚌,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苇羡,沒想到半個(gè)月后绸吸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡设江,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年锦茁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叉存。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡码俩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歼捏,到底是詐尸還是另有隱情稿存,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布瞳秽,位于F島的核電站挠铲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏寂诱。R本人自食惡果不足惜拂苹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痰洒。 院中可真熱鬧瓢棒,春花似錦、人聲如沸丘喻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泉粉。三九已至连霉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗡靡,已是汗流浹背跺撼。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留讨彼,地道東北人歉井。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像哈误,于是被迫代替她去往敵國(guó)和親哩至。 傳聞我的和親對(duì)象是個(gè)殘疾皇子躏嚎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容