/** * Commit your preferences changes back from this Editor to the * {@link SharedPreferences} object it is editing. This atomically * performs the requested modifications, replacing whatever is currently * in the SharedPreferences. * * <p>Note that when two editors are modifying preferences at the same * time, the last one to call commit wins. * * <p>If you don't care about the return value and you're * using this from your application's main thread, consider * using {@link #apply} instead. * * @return Returns true if the new values were successfully written * to persistent storage. */ booleancommit();
/** * Commit your preferences changes back from this Editor to the * {@link SharedPreferences} object it is editing. This atomically * performs the requested modifications, replacing whatever is currently * in the SharedPreferences. * * <p>Note that when two editors are modifying preferences at the same * time, the last one to call apply wins. * * <p>Unlike {@link #commit}, which writes its preferences out * to persistent storage synchronously, {@link #apply} * commits its changes to the in-memory * {@link SharedPreferences} immediately but starts an * asynchronous commit to disk and you won't be notified of * any failures. If another editor on this * {@link SharedPreferences} does a regular {@link #commit} * while a {@link #apply} is still outstanding, the * {@link #commit} will block until all async commits are * completed as well as the commit itself. * * <p>As {@link SharedPreferences} instances are singletons within * a process, it's safe to replace any instance of {@link #commit} with * {@link #apply} if you were already ignoring the return value. * * <p>You don't need to worry about Android component * lifecycles and their interaction with <code>apply()</code> * writing to disk. The framework makes sure in-flight disk * writes from <code>apply()</code> complete before switching * states. * * <p class='note'>The SharedPreferences.Editor interface * isn't expected to be implemented directly. However, if you * previously did implement it and are now getting errors * about missing <code>apply()</code>, you can simply call * {@link #commit} from <code>apply()</code>. */ voidapply(); }
// It's important that we always signal waiters, even if we'll make // them fail with an exception. The try-finally is pretty wide, but // better safe than sorry. try { if (thrown == null) { if (map != null) { mMap = map; mStatTimestamp = stat.st_mtim; mStatSize = stat.st_size; } else { mMap = new HashMap<>(); } } // In case of a thrown exception, we retain the old map. That allows // any open editors to commit and store updates. } catch (Throwable t) { mThrowable = t; } finally { mLock.notifyAll(); } } }
public String getString(String key, @Nullable String defValue){ synchronized (mLock) { awaitLoadedLocked(); String v = (String)mMap.get(key); return v != null ? v : defValue; } }
privatevoidawaitLoadedLocked(){ if (!mLoaded) { // Raise an explicit StrictMode onReadFromDisk for this // thread, since the real read will be in a different // thread and otherwise ignored by StrictMode. BlockGuard.getThreadPolicy().onReadFromDisk(); } while (!mLoaded) { try { mLock.wait(); } catch (InterruptedException unused) { } } if (mThrowable != null) { thrownew IllegalStateException(mThrowable); } }
public Editor edit(){ // TODO: remove the need to call awaitLoadedLocked() when // requesting an editor. will require some work on the // Editor, but then we should be able to do: // // context.getSharedPreferences(..).edit().putString(..).apply() // ... all without blocking. synchronized (mLock) { awaitLoadedLocked(); }
// 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的数据不能被修改,只能再拷贝一份 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()); }
// 这是清除标记,调用Editor.clear()时设置该标记为true if (mClear) { if (!mapToWriteToDisk.isEmpty()) { changesMade = true; // 清除从mMap读取的数据,即从xml中读取的数据 mapToWriteToDisk.clear(); } mClear = false; }
// 把mModified中键值对读入mapToWriteToDisk。 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. // this值,是调用Editor.remove的时候设置的。 if (v == this || v == null) { if (!mapToWriteToDisk.containsKey(k)) { continue; } // 从现有数据中,移除对象k mapToWriteToDisk.remove(k); } else { if (mapToWriteToDisk.containsKey(k)) { Object existingValue = mapToWriteToDisk.get(k); if (existingValue != null && existingValue.equals(v)) { continue; } } mapToWriteToDisk.put(k, v); }
privatevoidwriteToFile(MemoryCommitResult mcr, boolean isFromSyncCommit){ // ... 省略debug code,跟耗时计算相关 // 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) { // commit时进来 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; } } } }
// Attempt to write the file, delete the backup and return true as atomically as // possible. If any exception occurs, delete the new file; next time we will restore // from the backup. try { FileOutputStream str = createFileOutputStream(mFile); if (str == null) { mcr.setDiskWriteResult(false, false); return; } // 写文件 XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str); FileUtils.sync(str); str.close(); ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0); try { final StructStat stat = Os.stat(mFile.getPath()); synchronized (mLock) { // 记录时间戳和文件大小 mStatTimestamp = stat.st_mtim; mStatSize = stat.st_size; } } catch (ErrnoException e) { // Do nothing }
// Writing was successful, delete the backup file if there is one. xml文件写成功了,就删除.bak文件 mBackupFile.delete(); mDiskStateGeneration = mcr.memoryStateGeneration; mcr.setDiskWriteResult(true, true); return; } catch (XmlPullParserException e) { Log.w(TAG, "writeToFile: Got exception:", e); } catch (IOException e) { Log.w(TAG, "writeToFile: Got exception:", e); }
// Clean up an unsuccessfully written file if (mFile.exists()) { if (!mFile.delete()) { Log.e(TAG, "Couldn't clean up partially-written file " + mFile); } } mcr.setDiskWriteResult(false, false); }
这里如果写成功,则把.bak文件删除。
notifyListeners
执行结束之后,通知监听者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
privatevoidnotifyListeners(final MemoryCommitResult mcr){ if (mcr.listeners == null || mcr.keysModified == null || mcr.keysModified.size() == 0) { return; } if (Looper.myLooper() == Looper.getMainLooper()) { for (int i = mcr.keysModified.size() - 1; i >= 0; i--) { final String key = mcr.keysModified.get(i); for (OnSharedPreferenceChangeListener listener : mcr.listeners) { if (listener != null) { listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key); } } } } else { // Run this function on the main thread. ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr)); } }
// 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); }
/** * Used to make sure that only one thread is processing work items at a time. This means that * they are processed in the order added. * * This is separate from {@link #sLock} as this is held the whole time while work is processed * and we do not want to stall the whole class. */ privatestatic Object sProcessingWork = new Object(); privatestaticvoidprocessPendingWork(){ 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(); } } } }
/** * Trigger queued work to be processed immediately. The queued work is processed on a separate * thread asynchronous. While doing that run and process all finishers on this thread. The * finishers can be implemented in a way to check weather the queued work is finished. * * Is called from the Activity base class's onPause(), after BroadcastReceiver's onReceive, * after Service command handling, etc. (so async work is never lost) */ publicstaticvoidwaitToFinish(){ boolean hadMessages = false; Handler handler = getHandler();
synchronized (sLock) { if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) { // Delayed work will be processed at processPendingWork() below handler.removeMessages(QueuedWorkHandler.MSG_RUN); }
// We should not delay any work as this might delay the finishers sCanDelay = false; }