Android 开源项目DiskLruCache解析使用

DiskLruCache 硬盘缓存,非Google官方编写,但获得官方承认, 只需要下载下来放到项目中就行。

journal文件

这个日志文件,关系着DiskLruCache的正常使用,里面记录了每条缓存,下面看看里面信息


第一行是固定的字符串,第二行是DiskLruCache的版本号,这个值为1,第三行是APP的版本号,每当更新版本时会清除缓存,第四行是valueCount的值,在open时传入,一般为1,第五行是个空行。在下面就是缓存的信息

分别有几种前缀,DIRTY脏数据,当我们调用DiskLruCache的edit()方法时就会写入一个DIRTY,后面是缓存的key;CLEAN,当调用了commit方法后,会写入一条CLEAN;REMOVE,当调用absort方法,写入缓存失败,会产生一条REMOVE;READ,当读取一条缓存时会写入一条READ。


open获得实例

DiskLruCache 不是new出来的,而是通过一个open函数获得实例

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

        //根据参数创建对象赋值
        DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
        //判断日志文件是否存在
        if (cache.journalFile.exists()) {
            try {
                //读取日志文件
                cache.readJournal();
                cache.processJournal();
                //写日志文件的Writer
                cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true),
                        IO_BUFFER_SIZE);
                return cache;
            } catch (IOException journalIsCorrupt) {
//                System.logW("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;

    }
主要就是创建对象,读取或者创建日志文件。

看看void readJournal()

 private void readJournal() throws IOException {
        InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE);
        try {
           //这一段一般是文件头的信息
            String magic = readAsciiLine(in);
            String version = readAsciiLine(in);
            String appVersionString = readAsciiLine(in);
            String valueCountString = readAsciiLine(in);
            String blank = readAsciiLine(in);
            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 + "]");
            }

            //开始读日志信息
            while (true) {
                try {
                    //解析每一行的具体信息
                    readJournalLine(readAsciiLine(in));
                } catch (EOFException endOfJournal) {
                    break;
                }
            }
        } finally {
            //最后关闭输入流
            closeQuietly(in);
        }
    }
这个函数就是读取日志文件,前几行是日志的一些头信息,后面while循环解析日志,就是缓存的信息,解析出来放到了一个hash链表里面存储。

void rebuildJournal()

private synchronized void rebuildJournal() throws IOException {
        if (journalWriter != null) {
            journalWriter.close();
        }
        //writer指向的journalFileTmp
        Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE);
        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');
            }
        }

        writer.close();
        journalFileTmp.renameTo(journalFile);
        journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE);
    }
主要就是创建journal

写入缓存

private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
        //校验journalWriter是否为空
        checkNotClosed();
        //校验key的合法性
        validateKey(key);
        Entry entry = lruEntries.get(key);
        if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
                && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
            return null; // snapshot is stale
        }
        //如果entry为空,创建一个并加入map里面
        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;

        // 写一条DIRTY
        journalWriter.write(DIRTY + ' ' + key + '\n');
        journalWriter.flush();
        return editor;
    }
获得输出流

public OutputStream newOutputStream(int index) throws IOException {
            synchronized (DiskLruCache.this) {
                if (entry.currentEditor != this) {
                    throw new IllegalStateException();
                }
                return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
            }
        }
commit()操作

 public void commit() throws IOException {
        if (hasErrors) {
            completeEdit(this, false);
            remove(entry.key); // the previous entry is stale
        } else {
            completeEdit(this, true);
        }
    }

    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++) {
                //不存在dirty类型的,则停止,抛出异常
                if (!entry.getDirtyFile(i).exists()) {
                    editor.abort();
                    throw new IllegalStateException("edit didn't create file " + i);
                }
            }
        }

        for (int i = 0; i < valueCount; i++) {
            //获得dirty类型的File
            File dirty = entry.getDirtyFile(i);
            if (success) {
                if (dirty.exists()) {
                    //dirty转化为clean
                    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');
        }

        //当缓存过大时,重新创建
        if (size > maxSize || journalRebuildRequired()) {
            executorService.submit(cleanupCallable);
        }
    }

获取缓存

 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!
            return null;
        }
        
        redundantOpCount++;
        //取出缓存后添加一条READ
        journalWriter.append(READ + ' ' + key + '\n');
        //检查是否需要重构journal
        if (journalRebuildRequired()) {
            executorService.submit(cleanupCallable);
        }

        return new Snapshot(key, entry.sequenceNumber, ins);
    }
返回SnapShot对象,通过这个对象就可以获得缓存的输入流。 


其他一些方法

 remove(String key) 

移除特定的缓存,一般不使用,因为当缓存到达一定大小时,会去自定清理长期没用过的缓存
flush

同步内存的操作到journal文件中

delete()

删除所有缓存数据

close()

关闭DiskLruCache

小Demo


通过网络获取一张图片,缓存起来,点击按钮,通过缓存加载图片。






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值