Android 开源项目DiskLruCache解析使用

来源:互联网 发布:mysql存储小说内容 编辑:程序博客网 时间:2024/05/22 12:55

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
原创粉丝点击