)) {
Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mapToWriteToDisk.put(k, v);
}
changesMade = true;
if (hasListeners) {
keysModified.add(k);
}
}
mModified.clear();
if (changesMade) {
mCurrentMemoryStateGeneration++;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
mapToWriteToDisk);
}
简单说,commitToMemory()
方法会将所有需要改动的数据 mModified
和原 sp 文件数据 mMap
进行合并生成一个新的数据集合 mapToWriteToDisk
,从名字也可以看出来,这就是之后要写入文件的数据集。没错,SharedPreferences 的写入都是全量写入。即使你只改动了其中一个配置项,也会重新写入所有数据。针对这一点,我们可以做的优化是,将需要频繁改动的配置项使用单独的 sp 文件进行存储,避免每次都要全量写入。
3.2.3 enqueueDiskWrite()
> SharedPreferencesImpl.java
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); // 见 3.2.3.1
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
// commit() 直接在当前线程进行写入操作
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}
// apply() 方法执行此处,由 QueuedWork.QueuedWorkHandler 处理
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
回头先看一下 commit()
方法中是如何调用 enqueueDiskWrite()
方法的:
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, null);
第二个参数 postWriteRunnable
是 null
,所以 isFromSyncCommit
为 true
,会执行上面的 if
代码块,而不执行 QueuedWork.queue()
。由此可见,commit()
方法最后的写文件操作是直接在当前调用线程执行的,你在主线程调用该方法,就会直接在主线程进行 IO 操作。显然,这是不建议的,可能造成卡顿或者 ANR。在实际使用中我们应该尽量使用 apply()
方法来提交数据。当然,apply()
也并不是十全十美的,后面我们会提到。
3.2.3.1 writeToFile()
commit()
方法的最后一步了,将 mapToWriteToDisk
写入 sp 文件。
> SharedPreferencesImpl.java
private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
long startTime = 0;
long existsTime = 0;
long backupExistsTime = 0;
long outputStreamCreateTime = 0;
long writeTime = 0;
long fsyncTime = 0;
long setPermTime = 0;
long fstatTime = 0;
long deleteTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
boolean fileExists = mFile.exists();
if (DEBUG) {
existsTime = System.currentTimeMillis();
// Might not be set, hence init them to a default value
backupExistsTime = existsTime;
}
// Rename the current file so it may be used as a backup during the next read
if (fileExists) {
boolean needsWrite = false;
// Only need to write if the disk state is older than this commit
// 仅当磁盘状态比当前提交旧时草需要写入文件
if (mDiskStateGeneration < mcr.memoryStateGeneration) {
if (isFromSyncCommit) {
needsWrite = true;
} else {
synchronized (mLock) {
// No need to persist intermediate states. Just wait for the latest state to
// be persisted.
if (mCurrentMemoryStateGeneration == mcr.memoryStateGeneration) {
needsWrite = true;
}
}
}
}
if (!needsWrite) { // 无需写入,直接返回
mcr.setDiskWriteResult(false, true);
return;
}
boolean backupFileExists = mBackupFile.exists(); // 备份文件是否存在
if (DEBUG) {
backupExistsT