1.先说区别:
- commit()提交成功返回true,apply()提交没有返回值
- apply()提交失败的时候,不会有任何提示
- 都会把数据先原子提交(什么是原子提交:就是事务的所有修改动作,要么全部发生,要么一个都不发生)到内存,然后apply()异步提交到磁盘上。而commit()是同步提交到磁盘上。内存存储都是更新Map的值(具体请看下面?源码)。
2.再分析优劣
a.针对效率上来说:apply()要比commit()高效,因为当有多次提交的时候,因为写入磁盘耗时并且commit()是同步写入,如果前一个commit()没有执行完后面的都要等待(因为数据写入的过程中,为了保证数据写入的准确性,会用同步锁保证数据的安全性)
b.但是commit()能保证写入数据的成功性,而apply()只是调用了写入就返回了。如果在一些极端情况下(比如应用重启),apply()就可能造成数据写入失败了,但是我们并不知道失败了的情况。
总
结
\color{red}{总结}
总结:如果对结果要求严格,就使用commit()。如果对结果不做强烈要求,但是更看重性能,那就使用apply()。
下面是相关的源码:
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);
}
private MemoryCommitResult commitToMemory() {
MemoryCommitResult mcr = new MemoryCommitResult();
synchronized (SharedPreferencesImpl.this) {
...
synchronized (this) {
...
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
if (v == this || v == null) {
if (!mMap.containsKey(k)) {
continue;
}
mMap.remove(k);
} else {
if (mMap.containsKey(k)) {
Object existingValue = mMap.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mMap.put(k, v);
}
...
}
mModified.clear();
}
}
return mcr;
}
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);
}