源码理解apply()与commit()的区别
为什么SharedPreference保存数据首选apply()?commit()和apply()有什么区别?
①commit()是同步的:将数据直接写入存储;apply()是异步的,先将修改提交到内存不会阻塞,之后再写入存储。
②commit()有返回值标识是否成功写入;而apply()没有返回值。
现在来看看源码到底是如何实现apply()和commit()方法的。
1、SharedPreference接口和SharedPreferencesImpl实现
SharedPreference保存数据借助SharedPreference中定义的接口SharedPreferences.Editor,它们都是接口,定义在android.content包中。
public interface Editor {
Editor putString(String key, @Nullable String value);
Editor putStringSet(String key, @Nullable Set<String> values);
Editor putInt(String key, int value);
Editor putLong(String key, long value);
Editor putFloat(String key, float value);
Editor putBoolean(String key, boolean value);
Editor remove(String key);
Editor clear();
boolean commit();
void apply();
}SharedPreference接口的实现类是SharedPreferencesImpl,而SharedPreferences.Editor接口的实现是SharedPreferencesImpl.EditorImpl类,在android.app包中。
2、apply()
定义在SharedPreferences.Editor接口中的方法,在SharedPreferencesImpl.EditorImpl类中实现该方法,来看看该方法的完整实现:
public void apply() {
final long startTime = System.currentTimeMillis();
//提交到内存
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
if (DEBUG && mcr.wasWritten) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " applied after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
};
//放入QueuedWork中的sFinishers队列
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);//移除QueuedWork中的sFinishers队列
}
};
//任务压入写磁盘的任务队列
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);
}①先commitToMemory()提交到内存并返回MemoryCommitResult实例保存修改的集合。
②再构造第一个Runnable任务,标记等待commit到磁盘。
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
if (DEBUG && mcr.wasWritten) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " applied after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
};writtenToDiskLatch是定义在MemoryCommitResult类中,当数据commit后会减一
// EditorImpl.commitToMemory()方法的返回值类型
private static class MemoryCommitResult {
...
final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
...
void setDiskWriteResult(boolean wasWritten, boolean result) {
this.wasWritten = wasWritten;
writeToDiskResult = result;
writtenToDiskLatch.countDown();
}
}③接着将放入QueuedWork中的sFinishers队列:
public static void addFinisher(Runnable finisher) {
synchronized (sLock) {
sFinishers.add(finisher);
}
}④再构造另一个Runnable任务postWriteRunnable,
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};先执行awaitCommit任务等待提交,再将其移除QueuedWork中的sFinishers队列:
public static void removeFinisher(Runnable finisher) {
synchronized (sLock) {
sFinishers.remove(finisher);
}
}⑤apply()方法最后将任务压入写磁盘的任务队列:
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
@Override
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr, isFromSyncCommit);
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}enqueueDiskWrite()最后调用了QueuedWork的queue()方法:是否延迟执行写入的Runnable队列。
public static void queue(Runnable work, boolean shouldDelay) {
Handler handler = getHandler();
synchronized (sLock) {
sWork.add(work);
if (shouldDelay && sCanDelay) {
handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
} else {
handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
}
}
}这里DELAY常量为异步延迟时间 100ms
/** 该延迟应尽可能大,但应低到几乎无法感知*/
private static final long DELAY = 100;向内部定义的QueuedWorkHandler发送延迟消息:
private static class QueuedWorkHandler extends Handler {
static final int MSG_RUN = 1;
QueuedWorkHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
if (msg.what == MSG_RUN) {
processPendingWork();
}
}
}最终processPendingWork()方法开始执行真正的写入磁盘任务。
private static void processPendingWork() {
long startTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
synchronized (sProcessingWork) {
LinkedList<Runnable> work;
synchronized (sLock) {
work = (LinkedList<Runnable>) sWork.clone();
sWork.clear();
// Remove all msg-s as all work will be processed now
getHandler().removeMessages(QueuedWorkHandler.MSG_RUN);
}
if (work.size() > 0) {
for (Runnable w : work) {
w.run();
}
if (DEBUG) {
Log.d(LOG_TAG, "processing " + work.size() + " items took " +
+(System.currentTimeMillis() - startTime) + " ms");
}
}
}
}3、commit()
commit()方法的实现如下,看着有没有和apply()很眼熟?
public boolean commit() {
long startTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
MemoryCommitResult mcr = commitToMemory();
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
} finally {
if (DEBUG) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " committed after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}注意区别,虽然也是先commitToMemory()写入内存。但是调用enqueueDiskWrite(mcr, null)传入的第二个参数为null,并不需要delay任务,前面刚刚提到!
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
//为true
final boolean isFromSyncCommit = (postWriteRunnable == null);
...
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}所以commit()执行到后面不会有延迟异步,而是立即写入磁盘。
public static void queue(Runnable work, boolean shouldDelay) {
Handler handler = getHandler();
synchronized (sLock) {
sWork.add(work);
if (false&& sCanDelay) {
handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
} else {
handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
}
}
}推荐博客:
SharedPreference的使用以及commit()和apply()的区别
关于SharedPreference.Editor的apply()和commit()方法异同
“背书”们只知其一,不知其二。就像三角形内角之和是多少,相信99%的人都能脱口而出:180°。再问为什么呢,又有几个能说出为什么三角形内角之和是180°?因为只是被告诉是这样:

