SharedPreferencesImpl.java (建议大家先找一篇系统介绍这个SharedPreferencesImpl源码的博客看一下)
apply方法源码:
public void apply() {
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
QueuedWork.add(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
public void run() {
awaitCommit.run();
QueuedWork.remove(awaitCommit);
}
};
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
// Okay to notify the listeners before it's hit disk
// because the listeners should always get the same
// SharedPreferences instance back, which has the
// changes reflected in memory.
notifyListeners(mcr);
}
这里只分析一点:awaitCommit 这个Runnable是干啥用的
run方法只有一句:mcr.writtenToDiskLatch,mcr是MemoryCommitResult类型的变量,源码如下:
// Return value from EditorImpl#commitToMemory()
private static class MemoryCommitResult {
public boolean changesMade; // any keys different?
public List<String> keysModified; // may be null
public Set<OnSharedPreferenceChangeListener> listeners; // may be null
public Map<?, ?> mapToWriteToDisk;
public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
public volatile boolean writeToDiskResult = false;
public void setDiskWriteResult(boolean result) {
writeToDiskResult = result;
writtenToDiskLatch.countDown();
}
}
首先明确的是writtenToDiskLatch是一个CountDownLatch,java里的一种锁,此处初始化参数为1,即writtenToDiskLatch = new CountDownLatch(1),当调用它的countDown()方法减至0时,则释放这个锁,从await方法处继续调用,注意MemroyCommitResult这个类的setDiskWriteResult方法是唯一调用countDown()的方法。注意,setDiskWriteResult方法会在执行写入磁盘逻辑后执行(无论写入成功还是失败,都会执行)。
先说结论:awaitCommit是用来防止多次apply,然后该activity调用onStop方法时(activity将要销毁时),用来保证每一次apply都能够最终执行将sharedpreferences写入磁盘的逻辑(无论写入成功还是失败,一定保证它执行一次)。
代码倒着看,ActivityThread.java类中handleStopActivity方法中有一句代码:QueuedWork.waitToFinish();
public static void waitToFinish() {
Runnable toFinish;
while ((toFinish = sPendingWorkFinishers.poll()) != null) {
toFinish.run();
}
}
如果,队列中还处理的toFinish(说明调用了apply方法,我们暂不去关注commit方法,并且sharedpreferences还未写完磁盘),则调用toFinish.run,toFinish是哪里来的呢?答案在apply方法中的QueuedWork.add(awaitCommit);此时,mcr.writtenToDiskLatch.await();线程阻塞,阻止了handleStopActivity方法继续执行,等到所有写磁盘过程结束,调用setDiskWriteResult,handleStopActivity方法继续执行,最终Activity销毁。
一句话概括:awaitCommit这个Runnable就是为了保证在Activity销毁之前,把所有的SharedPreferences的改动写入磁盘。