可以看到,apply() 和 commit() 的区别是在 commit() 把内容同步提交到了硬盘,而 apply() 是先立即把修改提交给了内存,然后开启了一个异步的线程提交到硬盘。commit() 会接收 MemoryCommitResult 里面的一个 boolean 参数作为结果,而 apply() 没有对结果做任何关心。
我们可以看到,文件写入更新的操作都是交给 commitToMemory() 做的,这个方法返回了一个 MemoryCommitResult 对象,我们来看看到底做了什么。
// Returns true if any changes were made private MemoryCommitResult commitToMemory() { long memoryStateGeneration; List<String> keysModified = null; Set<OnSharedPreferenceChangeListener> listeners = null; Map<String, Object> mapToWriteToDisk; synchronized (SharedPreferencesImpl.this.mLock) { // We optimistically don't make a deep copy until // a memory commit comes in when we're already // writing to disk. if (mDiskWritesInFlight > 0) { // We can't modify our mMap as a currently // in-flight write owns it. Clone it before // modifying it. // noinspection unchecked mMap = new HashMap<String, Object>(mMap); } mapToWriteToDisk = mMap; mDiskWritesInFlight++; boolean hasListeners = mListeners.size() > 0; if (hasListeners) { keysModified = new ArrayList<String>(); listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); } synchronized (mEditorLock) { boolean changesMade = false; if (mClear) { if (!mapToWriteToDisk.isEmpty()) { changesMade = true; mapToWriteToDisk.clear(); } mClear = false; } for (Map.Entry<String, Object> e : mModified.entrySet()) { String k = e.getKey(); Object v = e.getValue(); // "this" is the magic value for a removal mutation. In addition, // setting a value to "null" for a given key is specified to be // equivalent to calling remove on that key. if (v == this || v == null) { if (!mapToWriteToDisk.containsKey(k)) { continue; } mapToWriteToDisk.remove(k); } else { if (mapToWriteToDisk.containsKey(k)) { 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); }可以看到,我们这里的 mMap 即存放当前 SharedPreferences 文件中的键值对,而 mModified 则存放的是当时 edit() 时 put 进去的键值对,这个我们前面有所介绍。这里有个 mDiskWritesInFlight 看起来应该是表示正在等待写的操作数量。
接下来我们首先处理了 edit().clear() 操作的 mClear 标志,当我们在外面调用 clear() 方法的时候,我们会把 mClear 设置为 true,这时候我们会直接通过 mMap.clear() 清空此时文件中的键值对,然后再遍历 mModified 中新 put 进来的键值对数据放到 mMap 中。也就是说:在一次提交中,如果我们又有 put 又有 clear() 操作的话,我们只能 clear() 掉之前的键值对,这次 put() 进去的键值对还是会被写入到 XML 文件中。
// 读取 val sharedPreferences = getSharedPreferences("123", Context.MODE_PRIVATE) // 写入 val editor = sharedPreferences.edit() editor.putInt("1", 123) editor.clear() editor.apply() Log.e("nanchen2251", "${sharedPreferences.getInt("1", 0)}")也就是说,当我们编写下面的代码的时候,得到的打印还是 123。
然后我们接着往下看,又发现了另外一个 commit() 和 apply() 都做了调用的方法是 enqueueDiskWrite()。
/** * Enqueue an already-committed-to-memory result to be written * to disk. * * They will be written to disk one-at-a-time in the order * that they're enqueued. * * @param postWriteRunnable if non-null, we're being called * from apply() and this is the runnable to run after * the write proceeds. if null (from a regular commit()), * then we're allowed to do this disk write on the main * thread (which in addition to reducing allocations and * creating a background thread, this has the advantage that * we catch them in userdebug StrictMode reports to convert * them where possible to 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); }