对于Android开发者来说,对于sharedPreferences并不陌生,用于存储轻量级的数据,而存储的时候,会用到Editor,在API 9之前,提交的时候用用到了editor.commit()方法,而从API 9之后,新增了一个apply()方法,可能大家多多少少知道它是一个异步的提交,能提高IO性能。
而本篇文章主要就是从源码去分析,它是如何提高IO性能的。废话不多说,上代码,跟进代码大家看到,sharedPreferences只是一个接口,所有的实现逻辑都在它的实现类sharedPreferencesImpl里,首先看下commit()方法,看代码解释:
public boolean commit() {
MemoryCommitResult mcr = commitToMemory();//把数据提交到内存
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
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);
}
比较两个方法,首先都是先把数据同步提交到内存中,然后再执行下面的SharedPreferencesImpl.this.enqueueDiskWrite方法,而两个方法调用不同的地方是,commit第二个参数传进去是null,apply传进去的是postWriteRunnable。咱们看下enqueueDiskWrite()这个方法里面是怎么执行的。
private void enqueueDiskWrite(final MemoryCommitResult mcr,final Runnable postWriteRunnable) {
final Runnable writeToDiskRunnable = new Runnable() {
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr);
}
synchronized (SharedPreferencesImpl.this) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
final boolean isFromSyncCommit = (postWriteRunnable == null);
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (SharedPreferencesImpl.this) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}
QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
}
首先这个方法里先new 一个Runnable writeToDiskRunnable (注意这里,只是一个Runnbale,而不是new Thread/一个子线程,有部分同学可能看到Runnable以为就是一个子线程呢),然后这个writeToDiskRunnable的run方法里执行writeToFile的方法,就是正在的要写到sp文件的方法了。接着往下看,isFromSyncCommit变量,它就是跟着commit与apply传进来的postWriteRunnable是否为空判断的,所以这当是commit方法调用是,isFromSyncCommit为true,apply方法调用时,isFromSyncCommit为false,所以当isFromSyncCommit为true并且mDiskWritesInFlight为1时(commitToMemory赋值的),就在当前的线程里执行了writeToDiskRunnable 的writeToFile方法了,也就是说当commit()方法调用的时候,是在当前线程执行的,串行执行。当apply()方法调用的时候,直接把writeToDiskRunnable 添加到QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);队列中执行,也就是异步执行,所以说apply相对commit能提交IO性能。
好了,以上就是自己从源码角度去分析SharedPreferences的commit与apply区别,如有写不对的地方,欢迎各位指正,多谢!