@Override public SharedPreferences getSharedPreferences(String name, int mode){ // At least one application in the world actually passes in a null // name. This happened to work because when we generated the file name // we would stringify it to "null.xml". Nice. if (mPackageInfo.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) { if (name == null) { name = "null"; } }
/** * Map from package name, to preference name, to cached preferences. 静态的sSharedPrefsCache */ privatestatic ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
@Override public SharedPreferences getSharedPreferences(File file, int mode){ SharedPreferencesImpl sp; synchronized (ContextImpl.class) { //file -> SP 的map final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked(); sp = cache.get(file); if (sp == null) { checkMode(mode); if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) { if (isCredentialProtectedStorage() && !getSystemService(UserManager.class) .isUserUnlockingOrUnlocked(UserHandle.myUserId())) { thrownew IllegalStateException("SharedPreferences in credential encrypted " + "storage are not available until after user is unlocked"); } } sp = new SharedPreferencesImpl(file, mode); cache.put(file, sp); return sp; } }
//获取静态的sSharedPrefsCache private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked(){ if (sSharedPrefsCache == null) { sSharedPrefsCache = new ArrayMap<>(); }
final String packageName = getPackageName(); ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName); if (packagePrefs == null) { packagePrefs = new ArrayMap<>(); sSharedPrefsCache.put(packageName, packagePrefs); }
// 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();//通知其他线程加载完成了 } } }
@Override 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(); }
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++;//正在同步+1
boolean hasListeners = mListeners.size() > 0; if (hasListeners) { keysModified = new ArrayList<String>(); listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); }
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) {//为null删除mMap的数据 if (!mapToWriteToDisk.containsKey(k)) { continue; } mapToWriteToDisk.remove(k); } else {//更新mMap中的数据 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++; }
// 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); }
/** * 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(){ long startTime = System.currentTimeMillis(); 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);
if (DEBUG) { hadMessages = true; Log.d(LOG_TAG, "waiting"); } }
// We should not delay any work as this might delay the finishers sCanDelay = false; }