Android4,移动端跨平台开发大型项目

int count;

while ((count = reader.read(buffer)) != -1) {

writer.write(buffer, 0, count);

}

return writer.toString();

} finally {

reader.close();

}

}

/**

* Returns the ASCII characters up to but not including the next “\r\n”, or

* “\n”.

*

* @throws java.io.EOFException if the stream is exhausted before the next newline

*     character.

*  读取输入流中返回的某行ASCII码字符

*/

public static String readAsciiLine(InputStream in) throws IOException {

// TODO: support UTF-8 here instead

StringBuilder result = new StringBuilder(80);

while (true) {

int c = in.read();

if (c == -1) {

throw new EOFException();

} else if (c == ‘\n’) {

break;

}

result.append((char) c);

}

int length = result.length();

if (length > 0 && result.charAt(length - 1) == ‘\r’) {

result.setLength(length - 1);

}

return result.toString();

}

/**

* Closes ‘closeable’, ignoring any checked exceptions. Does nothing if ‘closeable’ is null.

* closeable关闭

*/

public static void closeQuietly(Closeable closeable) {

if (closeable != null) {

try {

closeable.close();

} catch (RuntimeException rethrown) {

throw rethrown;

} catch (Exception ignored) {

}

}

}

/**

* Recursively delete everything in {@code dir}.

* 递归删除dir

*/

// TODO: this should specify paths as Strings rather than as Files

public static void deleteContents(File dir) throws IOException {

File[] files = dir.listFiles();

if (files == null) {

throw new IllegalArgumentException("not a directory: " + dir);

}

for (File file : files) {

if (file.isDirectory()) {

deleteContents(file);

}

if (!file.delete()) {

throw new IOException("failed to delete file: " + file);

}

}

}

/** This cache uses a single background thread to evict entries.

*  后台单线程回收entry

*/

private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,

60L, TimeUnit.SECONDS, new LinkedBlockingQueue());

private final Callable cleanupCallable = new Callable() {

@Override public Void call() throws Exception {

synchronized (DiskLruCache.this) {

if (journalWriter == null) {

return null; // closed

}

trimToSize();//回收到满足maxsize

if (journalRebuildRequired()) {

rebuildJournal();

redundantOpCount = 0;

}

}

return null;

}

};

//构造器

private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {

this.directory = directory;

this.appVersion = appVersion; Android4,移动端跨平台开发大型项目

this.journalFile = new File(directory, JOURNAL_FILE);

this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);

this.valueCount = valueCount;

this.maxSize = maxSize;

}

/**

* Opens the cache in {@code directory}, creating a cache if none exists

* there.

* 创建cache

*

* @param directory a writable directory

* @param appVersion

* @param valueCount the number of values per cache entry. Must be positive.

* 每一个key相对应的value的数目

* @param maxSize the maximum number of bytes this cache should use to store

* @throws IOException if reading or writing the cache directory fails

*/

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

throws IOException {

if (maxSize <= 0) {//maxsize必须大于0

throw new IllegalArgumentException(“maxSize <= 0”);

}

if (valueCount <= 0) {//valuecount也必须大于0

throw new IllegalArgumentException(“valueCount <= 0”);

}

// prefer to pick up where we left off优先处理先前的cache

DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);

if (cache.journalFile.exists()) {

try {

cache.readJournal();

cache.processJournal();

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创建一个空新的cache

directory.mkdirs();

cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);

cache.rebuildJournal();

return cache;

}

//读取日志信息

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);//关闭输入流

}

}

//读取日志中某行日志信息

private void readJournalLine(String line) throws IOException {

String[] parts = line.split(" ");

if (parts.length < 2) {

throw new IOException("unexpected journal line: " + line);

}

String key = parts[1];

if (parts[0].equals(REMOVE) && parts.length == 2) {

lruEntries.remove(key);

return;

}

Entry entry = lruEntries.get(key);

if (entry == null) {

entry = new Entry(key);

lruEntries.put(key, entry);

}

if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) {

entry.readable = true;

entry.currentEditor = null;

entry.setLengths(copyOfRange(parts, 2, parts.length));

} else if (parts[0].equals(DIRTY) && parts.length == 2) {

entry.currentEditor = new Editor(entry);

} else if (parts[0].equals(READ) && parts.length == 2) {

// 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.

* 处理日志

* 计算初始化cache的初始化大小和收集垃圾。Dirty entry假定不一致将会被删掉。

*/

private void processJournal() throws IOException {

deleteIfExists(journalFileTmp);//删除日志文件

for (Iterator i = lruEntries.values().iterator(); i.hasNext(); ) {

Entry entry = i.next();

if (entry.currentEditor == null) {

for (int t = 0; t < valueCount; t++) {

size += entry.lengths[t];

}

} else {

entry.currentEditor = null;

for (int t = 0; t < valueCount; t++) {

deleteIfExists(entry.getCleanFile(t));

deleteIfExists(entry.getDirtyFile(t));

}

i.remove();

}

}

}

/**

* Creates a new journal that omits redundant information. This replaces the

* current journal if it exists.

* 创建一个新的删掉冗余信息的日志。替换当前的日志

*/

private synchronized void rebuildJournal() throws IOException {

if (journalWriter != null) {

journalWriter.close();

}

Writer writer = new BufferedWriter(new 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);

}

//文件若存在删除

private static void deleteIfExists(File file) throws IOException {

//        try {

//            Libcore.os.remove(file.getPath());

//        } catch (ErrnoException errnoException) {

//            if (errnoException.errno != OsConstants.ENOENT) {

//                throw errnoException.rethrowAsIOException();

//            }

//        }

if (file.exists() && !file.delete()) {

throw new IOException();

}

}

/**

* Returns a snapshot of the entry named {@code key}, or null if it doesn’t

* exist is not currently readable. If a value is returned, it is moved to

* the head of the LRU queue.

* 返回key对应的entry的snapshot,当key相应的entry不存在或者当前不可读时返回null。

* 如果返回相应的值,它就会被移动到LRU队列的头部。

*/

public synchronized Snapshot get(String key) throws IOException {

checkNotClosed();//检查cache是否已关闭

validateKey(key);//验证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++;

journalWriter.append(READ + ’ ’ + key + ‘\n’);

if (journalRebuildRequired()) {

executorService.submit(cleanupCallable);

}

return new Snapshot(key, entry.sequenceNumber, ins);

}

/**

* Returns an editor for the entry named {@code key}, or null if another

* edit is in progress.

*/

public Editor edit(String key) throws IOException {

return edit(key, ANY_SEQUENCE_NUMBER);

}

//有key和序列号生成一个editor

private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {

checkNotClosed();//检查cache关闭与否

validateKey(key);//验证key格式正确性

Entry entry = lruEntries.get(key);

if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER

&& (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {

return null; // snapshot is stale

}

if (entry == null) {

entry = new Entry(key);

lruEntries.put(key, entry);

} else if (entry.currentEditor != null) {

return null; // another edit is in progress

}

Editor editor = new Editor(entry);

entry.currentEditor = editor;

// flush the journal before creating files to prevent file leaks

journalWriter.write(DIRTY + ’ ’ + key + ‘\n’);

journalWriter.flush();

return editor;

}

/**

* Returns the directory where this cache stores its data.

*/

public File getDirectory() {

return directory;

}

/**

* Returns the maximum number of bytes that this cache should use to store

* its data.

*/

public long maxSize() {

return maxSize;

}

/**

* Returns the number of bytes currently being used to store the values in

* this cache. This may be greater than the max size if a background

* deletion is pending.

*/

public synchronized long size() {

return size;

}

//完成Edit动作

private synchronized void completeEdit(Editor editor, boolean success) throws IOException {

Entry entry = editor.entry;

if (entry.currentEditor != editor) {

throw new IllegalStateException();

}

// if this edit is creating the entry for the first time, every index must have a value

if (success && !entry.readable) {

for (int i = 0; i < valueCount; i++) {

if (!entry.getDirtyFile(i).exists()) {

editor.abort();

throw new IllegalStateException("edit didn’t create file " + i);

}

}

}

for (int i = 0; i < valueCount; i++) {

File dirty = entry.getDirtyFile(i);

if (success) {

if (dirty.exists()) {

File clean = entry.getCleanFile(i);

dirty.renameTo(clean);

long oldLength = entry.lengths[i];

long newLength = clean.length();

entry.lengths[i] = newLength;

size = size - oldLength + newLength;

}

} else {

deleteIfExists(dirty);

}

}

redundantOpCount++;

entry.currentEditor = null;

if (entry.readable | success) {

entry.readable = true;

journalWriter.write(CLEAN + ’ ’ + entry.key + entry.getLengths() + ‘\n’);

if (success) {

entry.sequenceNumber = nextSequenceNumber++;

}

} else {

lruEntries.remove(entry.key);

journalWriter.write(REMOVE + ’ ’ + entry.key + ‘\n’);

}

if (size > maxSize || journalRebuildRequired()) {

executorService.submit(cleanupCallable);

}

}

/**

* We only rebuild the journal when it will halve the size of the journal

* and eliminate at least 2000 ops.

* 当日志大小减半并且删掉至少2000项时重新构造日志

*/

private boolean journalRebuildRequired() {

final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000;

return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD

&& redundantOpCount >= lruEntries.size();

}

/**

* Drops the entry for {@code key} if it exists and can be removed. Entries

* actively being edited cannot be removed.

* 删除key相应的entry,被编辑的Entry不能被remove

* @return true if an entry was removed.

*/

public synchronized boolean remove(String key) throws IOException {

checkNotClosed();//检查cache是否已经关闭

validateKey(key);//验证key格式的正确性

Entry entry = lruEntries.get(key);

if (entry == null || entry.currentEditor != null) {

return false;

}

for (int i = 0; i < valueCount; i++) {

File file = entry.getCleanFile(i);

if (!file.delete()) {

throw new IOException("failed to delete " + file);

}

size -= entry.lengths[i];

entry.lengths[i] = 0;

}

redundantOpCount++;

journalWriter.append(REMOVE + ’ ’ + key + ‘\n’);

lruEntries.remove(key);

if (journalRebuildRequired()) {

executorService.submit(cleanupCallable);

}

return true;

}

/**

* Returns true if this cache has been closed.

* 判断cache是否已经关闭

*/

public boolean isClosed() {

return journalWriter == null;

}

//检查cache是否已经关闭

private void checkNotClosed() {

if (journalWriter == null) {

throw new IllegalStateException(“cache is closed”);

}

}

/**

* Force buffered operations to the filesystem.

*/

public synchronized void flush() throws IOException {

checkNotClosed();//检查cache是否关闭

trimToSize();//满足最大空间limit

journalWriter.flush();

}

/**

* Closes this cache. Stored values will remain on the filesystem.

* 关闭cache。

*/

public synchronized void close() throws IOException {

if (journalWriter == null) {

return; // already closed

}

for (Entry entry : new ArrayList(lruEntries.values())) {

if (entry.currentEditor != null) {

entry.currentEditor.abort();

}

}

trimToSize();

journalWriter.close();

journalWriter = null;

}

//回收删除某些entry到空间大小满足maxsize

private void trimToSize() throws IOException {

while (size > maxSize) {

//            Map.Entry<String, Entry> toEvict = lruEntries.eldest();

final Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();

remove(toEvict.getKey());

}

}

/**

* Closes the cache and deletes all of its stored values. This will delete

* all files in the cache directory including files that weren’t created by

* the cache.

* 关闭删除cache

*/

public void delete() throws IOException {

close();

deleteContents(directory);

}

//验证key格式的正确性

private void validateKey(String key) {

if (key.contains(" “) || key.contains(”\n") || key.contains("\r")) {

throw new IllegalArgumentException(

“keys must not contain spaces or newlines: \”" + key + “\”");

}

}

//字符串形式读出输入流的内容

private static String inputStreamToString(InputStream in) throws IOException {

return readFully(new InputStreamReader(in, UTF_8));

}

/**

* A snapshot of the values for an entry.

* entry的快照

*/

public final class Snapshot implements Closeable {

private final String key;//key

private final long sequenceNumber;//序列号(同文件名称)

private final InputStream[] ins;//两个修改的文件输入流

private Snapshot(String key, long sequenceNumber, InputStream[] ins) {

this.key = key;

this.sequenceNumber = sequenceNumber;

this.ins = ins;

}

/**

* Returns an editor for this snapshot’s entry, or null if either the

* entry has changed since this snapshot was created or if another edit

* is in progress.

* 返回entry快照的editor,如果entry已经更新了或者另一个edit正在处理过程中返回null。

*/

public Editor edit() throws IOException {

return DiskLruCache.this.edit(key, sequenceNumber);

}

/**

* Returns the unbuffered stream with the value for {@code index}.

*/

public InputStream getInputStream(int index) {

return ins[index];

}

/**

* Returns the string value for {@code index}.

*/

public String getString(int index) throws IOException {

return inputStreamToString(getInputStream(index));

}

@Override public void close() {

for (InputStream in : ins) {

closeQuietly(in);

}

}

}

/**

* Edits the values for an entry.

* entry编辑器
final class Snapshot implements Closeable {

private final String key;//key

private final long sequenceNumber;//序列号(同文件名称)

private final InputStream[] ins;//两个修改的文件输入流

private Snapshot(String key, long sequenceNumber, InputStream[] ins) {

this.key = key;

this.sequenceNumber = sequenceNumber;

this.ins = ins;

}

/**

* Returns an editor for this snapshot’s entry, or null if either the

* entry has changed since this snapshot was created or if another edit

* is in progress.

* 返回entry快照的editor,如果entry已经更新了或者另一个edit正在处理过程中返回null。

*/

public Editor edit() throws IOException {

return DiskLruCache.this.edit(key, sequenceNumber);

}

/**

* Returns the unbuffered stream with the value for {@code index}.

*/

public InputStream getInputStream(int index) {

return ins[index];

}

/**

* Returns the string value for {@code index}.

*/

public String getString(int index) throws IOException {

return inputStreamToString(getInputStream(index));

}

@Override public void close() {

for (InputStream in : ins) {

closeQuietly(in);

}

}

}

/**

* Edits the values for an entry.

* entry编辑器

上一篇:SqlServer批量导入C#100万条数据


下一篇:循环遍历Map的4中方法