Android4,想搞定大厂面试官

* observe the value at the time that {@link #get} was called. Updates and

* removals after the call do not impact ongoing reads.

* 调用get来读entry的快照。当get调用时读者读其值,更新或者删除不会影响先前的读

*

*

This class is tolerant of some I/O errors. If files are missing from the

* filesystem, the corresponding entries will be dropped from the cache. If

* an error occurs while writing a cache value, the edit will fail silently.

* Callers should handle other problems by catching {@code IOException} and

* responding appropriately.

* 该类可以容忍一些I/O errors。如果文件丢失啦,相应的entry就会被drop。写cache时如果error发生,edit将失败。

* 调用者应当相应的处理其它问题

*/

public final class DiskLruCache implements Closeable {

static final String JOURNAL_FILE = “journal”;

static final String JOURNAL_FILE_TMP = “journal.tmp”;

static final String MAGIC = “libcore.io.DiskLruCache”;

static final String VERSION_1 = “1”;

static final long ANY_SEQUENCE_NUMBER = -1;

private static final String CLEAN = “CLEAN”;

private static final String DIRTY = “DIRTY”;

private static final String REMOVE = “REMOVE”;

private static final String READ = “READ”;

private static final Charset UTF_8 = Charset.forName(“UTF-8”);

private static final int IO_BUFFER_SIZE = 8 * 1024;//8K

/*

* This cache uses a journal file named “journal”. A typical journal file

* looks like this:

*     libcore.io.DiskLruCache

*     1           //the disk cache’s version

*     100         //the application’s version

*     2           //value count

*

*    //state  key                            optional

*     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054

*     DIRTY 335c4c6028171cfddfbaae1a9c313c52

*     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342

*     REMOVE 335c4c6028171cfddfbaae1a9c313c52

*     DIRTY 1ab96a171faeeee38496d8b330771a7a

*     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234

*     READ 335c4c6028171cfddfbaae1a9c313c52

*     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6

*

* The first five lines of the journal form its header. They are the

* constant string “libcore.io.DiskLruCache”, the disk cache’s version,

* the application’s version, the value count, and a blank line.

*

* Each of the subsequent lines in the file is a record of the state of a

* cache entry. Each line contains space-separated values: a state, a key,

* and optional state-specific values.

*   o DIRTY lines track that an entry is actively being created or updated.

*     Every successful DIRTY action should be followed by a CLEAN or REMOVE

*     action. DIRTY lines without a matching CLEAN or REMOVE indicate that

*     temporary files may need to be deleted.

*     Dirty是entry被创建或者更新,每一个dirty应当被clean或remove action,如果有一行dirty没有

*     匹配的clean或Remove action,就表示临时文件需要被删除。

*   o CLEAN lines track a cache entry that has been successfully published

*     and may be read. A publish line is followed by the lengths of each of

*     its values.

*     Clean entry已经成功的发布并且可能会被读过。一个发布行

*   o READ lines track accesses for LRU.

*   Read表示LRU访问

*   o REMOVE lines track entries that have been deleted.

*   Remove表示entry已经被删除

*

* The journal file is appended to as cache operations occur. The journal may

* occasionally be compacted by dropping redundant lines. A temporary file named

* “journal.tmp” will be used during compaction; that file should be deleted if

* it exists when the cache is opened.

* 日志文件在cache操作发生时添加,日志可能O尔删除的冗余行来压缩。一个临时的名字为journal.tmp的文件将被使用

* 在压缩期间。当cache被opened的时候文件应当被删除。

*/

private final File directory;

private final File journalFile;//日志文件

private final File journalFileTmp;//日志文件临时文件

private final int appVersion;//应用ersion

private final long maxSize;//最大空间

private final int valueCount;//key对应的value的个数

private long size = 0;

private Writer journalWriter;

private final LinkedHashMap<String, Entry> lruEntries

= new LinkedHashMap<String, Entry>(0, 0.75f, true);

private int redundantOpCount;

/**

* To differentiate between old and current snapshots, each entry is given

* a sequence number each time an edit is committed. A snapshot is stale if

* its sequence number is not equal to its entry’s sequence number.

* 区分老的和当前的快照,每一个实体在每次编辑被committed时都被赋予一个序列号。

* 一个快照的序列号如果不等于entry的序列号那它就是废弃的。

*/

private long nextSequenceNumber = 0;

//数组拷贝

/* From java.util.Arrays */

@SuppressWarnings(“unchecked”)

private static T[] copyOfRange(T[] original, int start, int end) {

final int originalLength = original.length; // For exception priority compatibility.

if (start > end) {

throw new IllegalArgumentException();

}

if (start < 0 || start > originalLength) {

throw new ArrayIndexOutOfBoundsException();

}

final int resultLength = end - start;

final int copyLength = Math.min(resultLength, originalLength - start);

final T[] result = (T[]) Array

.newInstance(original.getClass().getComponentType(), resultLength);

System.arraycopy(original, start, result, 0, copyLength);

return result;

}

/**

* Returns the remainder of ‘reader’ as a string, closing it when done.

* 返回String的值,然后close

*/

public static String readFully(Reader reader) throws IOException {

try {

StringWriter writer = new StringWriter();

char[] buffer = new char[1024];

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;

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();

}

总结

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

上述【高清技术脑图】以及【配套的架构技术PDF】可以关注我免费获取

Android学习PDF+架构视频+面试文档+源码笔记

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

hrow new IllegalStateException();

}

总结

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-7hw57tv2-1710959971051)]

[外链图片转存中…(img-46rbSBHk-1710959971052)]

上述【高清技术脑图】以及【配套的架构技术PDF】可以关注我免费获取

Android学习PDF+架构视频+面试文档+源码笔记

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-IOnMf9To-1710959971052)]
[外链图片转存中…(img-TnCRu9ra-1710959971052)]
[外链图片转存中…(img-gpQOk4Va-1710959971053)]
[外链图片转存中…(img-yp4zq74c-1710959971053)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-HUR8KmmT-1710959971054)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值