MODE_WORLD_READABLE:API17被弃用。使用这个模式,所有的APP都可以对这个SharedPreferences进行读操作。所以这个模式被Android官方严厉警告禁止使用(It is strongly discouraged),并推荐使用ContentProvider、BroadcastReceiver和Service。
这个方法需要传入一个mode参数,这个参数和上面的context#getSharedPreferences()的mode参数是一样的。其实这个方法和上面Context的那个方法是一样的,他两都是调用的SharedPreferences getSharedPreferences(String name, int mode)。只不过Context的需要你去指定文件名,而这个方法你不需要手动去指定,而是会自动将当前Activity的类名作为了文件名。
@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. // ps:这个nice很精髓😂 if (mPackageInfo.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) { if (name == null) { name = "null"; } }
// 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 = newHashMap<>(); } } // 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(); } } }
@GuardedBy("mLock") 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) { thrownewIllegalStateException(mThrowable); } }
@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) { // ->>> 重点1 awaitLoadedLocked(); }
/** * 重点1:SharedPreferencesImpl # awaitLoadedLocked() */ @GuardedBy("mLock") 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) { thrownewIllegalStateException(mThrowable); } }
// 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); }
// Returns true if any changes were made private MemoryCommitResult commitToMemory() { // 当前Memory的状态,其实也就是当需要提交数据到内存的时候,他的值就加一 long memoryStateGeneration; List<String> keysModified = null; Set<OnSharedPreferenceChangeListener> listeners = null; // 存数据的Map 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 = newHashMap<String, Object>(mMap); } mapToWriteToDisk = mMap; // 2.2.3.5的关键点 mDiskWritesInFlight++;
for (Map.Entry<String, Object> e : mModified.entrySet()) { Stringk= e.getKey(); Objectv= 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)) { ObjectexistingValue= 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++; }
@Override public SharedPreferences getSharedPreferences(File file, int mode) { // ...前面的代码省略的,如果大家想回忆下,可以跳转到2.1节 if ((mode & Context.MODE_MULTI_PROCESS) != 0 || getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) { // If somebody else (some other process) changed the prefs // file behind our back, we reload it. This has been the // historical (if undocumented) behavior. // ->>> 重点1 sp.startReloadIfChangedUnexpectedly(); } return sp; }
/** * 重点1 SharedPreferencesImpl # startReloadIfChangedUnexpectedly() */ voidstartReloadIfChangedUnexpectedly() { synchronized (mLock) { // TODO: wait for any pending writes to disk? // ->>> 重点2 if (!hasFileChangedUnexpectedly()) { return; } // ->>> 重点3 startLoadFromDisk(); } }
/** * 重点2 SharedPreferencesImpl # hasFileChangedUnexpectedly() * 如果文件发生了预期之外的修改,也就是说有其他进程在修改,就返回true,否则false */ privatebooleanhasFileChangedUnexpectedly() { synchronized (mLock) { // 如果mDiskWritesInFlight大于0,就证明是在当前进程中修改的,那就不用重新读取 if (mDiskWritesInFlight > 0) { // If we know we caused it, it's not unexpected. if (DEBUG) Log.d(TAG, "disk write in flight, not unexpected."); returnfalse; } }
final StructStat stat; try { /* * Metadata operations don't usually count as a block guard * violation, but we explicitly want this one. */ BlockGuard.getThreadPolicy().onReadFromDisk(); stat = Os.stat(mFile.getPath()); } catch (ErrnoException e) { returntrue; }
@GuardedBy("mWritingToDiskLock") privatevoidwriteToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) { // ... booleanfileExists= mFile.exists(); // ... // Rename the current file so it may be used as a backup during the next read if (fileExists) { // ... booleanbackupFileExists= mBackupFile.exists(); // ... if (!backupFileExists) { if (!mFile.renameTo(mBackupFile)) { Log.e(TAG, "Couldn't rename file " + mFile + " to backup file " + mBackupFile); mcr.setDiskWriteResult(false, false); return; } } else { mFile.delete(); } }
// 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 { FileOutputStreamstr= createFileOutputStream(mFile); // ... XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);