//ContextWrapper.java@OverridepublicSharedPreferencesgetSharedPreferences(Stringname,intmode){returnmBase.getSharedPreferences(name,mode);}//ContextImpl.javaprivateArrayMap<String,File>mSharedPrefsPaths;@OverridepublicSharedPreferencesgetSharedPreferences(Stringname,intmode){// 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";}}Filefile;synchronized(ContextImpl.class){if(mSharedPrefsPaths==null){mSharedPrefsPaths=newArrayMap<>();}//查询是否存在相应文件file=mSharedPrefsPaths.get(name);if(file==null){//如果不存在则创建相应文件 并存入Map中file=getSharedPreferencesPath(name);mSharedPrefsPaths.put(name,file);}}returngetSharedPreferences(file,mode);}
@OverridepublicFilegetSharedPreferencesPath(Stringname){returnmakeFilename(getPreferencesDir(),name+".xml");}@UnsupportedAppUsageprivateFilegetPreferencesDir(){synchronized(mSync){if(mPreferencesDir==null){mPreferencesDir=newFile(getDataDir(),"shared_prefs");}returnensurePrivateDirExists(mPreferencesDir);}}@OverridepublicFilegetDataDir(){if(mPackageInfo!=null){Fileres=null;if(isCredentialProtectedStorage()){res=mPackageInfo.getCredentialProtectedDataDirFile();}elseif(isDeviceProtectedStorage()){res=mPackageInfo.getDeviceProtectedDataDirFile();}else{res=mPackageInfo.getDataDirFile();}if(res!=null){if(!res.exists()&&android.os.Process.myUid()==android.os.Process.SYSTEM_UID){Log.wtf(TAG,"Data directory doesn't exist for package "+getPackageName(),newThrowable());}returnres;}else{thrownewRuntimeException("No data directory found for package "+getPackageName());}}else{thrownewRuntimeException("No package details found for package "+getPackageName());}}//确保文件是否存在privatestaticFileensurePrivateDirExists(Filefile){returnensurePrivateDirExists(file,0771,-1,null);}privatestaticFileensurePrivateCacheDirExists(Filefile,Stringxattr){finalintgid=UserHandle.getCacheAppGid(Process.myUid());returnensurePrivateDirExists(file,02771,gid,xattr);}privatestaticFileensurePrivateDirExists(Filefile,intmode,intgid,Stringxattr){if(!file.exists()){finalStringpath=file.getAbsolutePath();try{Os.mkdir(path,mode);Os.chmod(path,mode);if(gid!=-1){Os.chown(path,-1,gid);}}catch(ErrnoExceptione){if(e.errno==OsConstants.EEXIST){// We must have raced with someone; that's okay}else{Log.w(TAG,"Failed to ensure "+file+": "+e.getMessage());}}if(xattr!=null){try{finalStructStatstat=Os.stat(file.getAbsolutePath());finalbyte[]value=newbyte[8];Memory.pokeLong(value,0,stat.st_ino,ByteOrder.nativeOrder());Os.setxattr(file.getParentFile().getAbsolutePath(),xattr,value,0);}catch(ErrnoExceptione){Log.w(TAG,"Failed to update "+xattr+": "+e.getMessage());}}}returnfile;}
@OverridepublicSharedPreferencesgetSharedPreferences(Filefile,intmode){SharedPreferencesImplsp;synchronized(ContextImpl.class){finalArrayMap<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())){thrownewIllegalStateException("SharedPreferences in credential encrypted "+"storage are not available until after user is unlocked");}}//创建SharedPreferencesImplsp=newSharedPreferencesImpl(file,mode);cache.put(file,sp);returnsp;}}//指定多进程模式,则当文件被其他进程修改时,则会重新加载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.sp.startReloadIfChangedUnexpectedly();}returnsp;}
privatevoidcheckMode(intmode){//7.0之后使用MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE会抛异常if(getApplicationInfo().targetSdkVersion>=Build.VERSION_CODES.N){if((mode&MODE_WORLD_READABLE)!=0){thrownewSecurityException("MODE_WORLD_READABLE no longer supported");}if((mode&MODE_WORLD_WRITEABLE)!=0){thrownewSecurityException("MODE_WORLD_WRITEABLE no longer supported");}}}
privatevoidloadFromDisk(){synchronized(mLock){if(mLoaded){return;}if(mBackupFile.exists()){mFile.delete();mBackupFile.renameTo(mFile);}}// Debuggingif(mFile.exists()&&!mFile.canRead()){Log.w(TAG,"Attempt to read preferences file "+mFile+" without permission");}Map<String,Object>map=null;StructStatstat=null;Throwablethrown=null;try{stat=Os.stat(mFile.getPath());if(mFile.canRead()){BufferedInputStreamstr=null;try{//读取流str=newBufferedInputStream(newFileInputStream(mFile),16*1024);//解析xmlmap=(Map<String,Object>)XmlUtils.readMapXml(str);}catch(Exceptione){Log.w(TAG,"Cannot read "+mFile.getAbsolutePath(),e);}finally{IoUtils.closeQuietly(str);}}}catch(ErrnoExceptione){// An errno exception means the stat failed. Treat as empty/non-existing by// ignoring.}catch(Throwablet){thrown=t;}synchronized(mLock){mLoaded=true;mThrowable=thrown;// 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(Throwablet){mThrowable=t;}finally{mLock.notifyAll();}}}
//SharedPreferencesImpl.java@Override@NullablepublicStringgetString(Stringkey,@NullableStringdefValue){synchronized(mLock){//检查是否加载完成awaitLoadedLocked();Stringv=(String)mMap.get(key);returnv!=null?v:defValue;}}@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(InterruptedExceptionunused){}}if(mThrowable!=null){thrownewIllegalStateException(mThrowable);}}
//SharedPreferencesImpl的edit方法@OverridepublicEditoredit(){// 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();}returnnewEditorImpl();}//EditorImpl是SharedPreferencesImpl的内部类publicfinalclassEditorImplimplementsEditor{privatefinalObjectmEditorLock=newObject();//创建EditorImpl会创建一个Map,并把这些put的数据存储到Map中,当commit的时候,遍历//map,并存到mMap中@GuardedBy("mEditorLock")privatefinalMap<String,Object>mModified=newHashMap<>();@GuardedBy("mEditorLock")privatebooleanmClear=false;@OverridepublicEditorputString(Stringkey,@NullableStringvalue){synchronized(mEditorLock){mModified.put(key,value);returnthis;}}@OverridepublicEditorremove(Stringkey){synchronized(mEditorLock){mModified.put(key,this);returnthis;}}@OverridepublicEditorclear(){synchronized(mEditorLock){mClear=true;returnthis;}}}
@Overridepublicbooleancommit(){longstartTime=0;if(DEBUG){startTime=System.currentTimeMillis();}//将数据更新到内存MemoryCommitResultmcr=commitToMemory();//将内存数据同步到文件SharedPreferencesImpl.this.enqueueDiskWrite(mcr,null/* sync write on this thread okay */);try{//进入等待状态,直到写入文件的操作完成mcr.writtenToDiskLatch.await();}catch(InterruptedExceptione){returnfalse;}finally{if(DEBUG){Log.d(TAG,mFile.getName()+":"+mcr.memoryStateGeneration+" committed after "+(System.currentTimeMillis()-startTime)+" ms");}}//通知监听notifyListeners(mcr);//返回文件操作的结果数据returnmcr.writeToDiskResult;}
// Returns true if any changes were madeprivateMemoryCommitResultcommitToMemory(){longmemoryStateGeneration;booleankeysCleared=false;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 uncheckedmMap=newHashMap<String,Object>(mMap);}//赋值mapToWriteToDisk=mMap;mDiskWritesInFlight++;booleanhasListeners=mListeners.size()>0;if(hasListeners){keysModified=newArrayList<String>();listeners=newHashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());}synchronized(mEditorLock){booleanchangesMade=false;if(mClear){if(!mapToWriteToDisk.isEmpty()){changesMade=true;mapToWriteToDisk.clear();}keysCleared=true;mClear=false;}//遍历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;}//包含,调用remove方法mapToWriteToDisk.remove(k);}else{//存在key 并且存入的value和将要存入的值相同则跳出本次循环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++;}memoryStateGeneration=mCurrentMemoryStateGeneration;}}returnnewMemoryCommitResult(memoryStateGeneration,keysCleared,keysModified,listeners,mapToWriteToDisk);}
privatevoidenqueueDiskWrite(finalMemoryCommitResultmcr,finalRunnablepostWriteRunnable){//判断是否是同步finalbooleanisFromSyncCommit=(postWriteRunnable==null);finalRunnablewriteToDiskRunnable=newRunnable(){@Overridepublicvoidrun(){synchronized(mWritingToDiskLock){//执行文件写入操作writeToFile(mcr,isFromSyncCommit);}synchronized(mLock){mDiskWritesInFlight--;}//commit传入为null不执行该方法if(postWriteRunnable!=null){postWriteRunnable.run();}}};// Typical #commit() path with fewer allocations, doing a write on// the current thread.if(isFromSyncCommit){booleanwasEmpty=false;synchronized(mLock){wasEmpty=mDiskWritesInFlight==1;}if(wasEmpty){writeToDiskRunnable.run();return;}}//同步不执行该方法QueuedWork.queue(writeToDiskRunnable,!isFromSyncCommit);}
privatevoidwriteToFile(MemoryCommitResultmcr,booleanisFromSyncCommit){longstartTime=0;longexistsTime=0;longbackupExistsTime=0;longoutputStreamCreateTime=0;longwriteTime=0;longfsyncTime=0;longsetPermTime=0;longfstatTime=0;longdeleteTime=0;if(DEBUG){startTime=System.currentTimeMillis();}booleanfileExists=mFile.exists();if(DEBUG){existsTime=System.currentTimeMillis();// Might not be set, hence init them to a default valuebackupExistsTime=existsTime;}// Rename the current file so it may be used as a backup during the next readif(fileExists){//判断是否需要写入booleanneedsWrite=false;// Only need to write if the disk state is older than this commitif(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;}}}}//不需要写入直接returnif(!needsWrite){mcr.setDiskWriteResult(false,true);return;}booleanbackupFileExists=mBackupFile.exists();if(DEBUG){backupExistsTime=System.currentTimeMillis();}//备份文件不存在 将当前文件名修改为备份文件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);if(DEBUG){outputStreamCreateTime=System.currentTimeMillis();}if(str==null){mcr.setDiskWriteResult(false,false);return;}XmlUtils.writeMapXml(mcr.mapToWriteToDisk,str);writeTime=System.currentTimeMillis();FileUtils.sync(str);fsyncTime=System.currentTimeMillis();str.close();ContextImpl.setFilePermissionsFromMode(mFile.getPath(),mMode,0);if(DEBUG){setPermTime=System.currentTimeMillis();}try{finalStructStatstat=Os.stat(mFile.getPath());synchronized(mLock){mStatTimestamp=stat.st_mtim;mStatSize=stat.st_size;}}catch(ErrnoExceptione){// Do nothing}if(DEBUG){fstatTime=System.currentTimeMillis();}// Writing was successful, delete the backup file if there is one.//写入成功删除备份文件mBackupFile.delete();if(DEBUG){deleteTime=System.currentTimeMillis();}mDiskStateGeneration=mcr.memoryStateGeneration;//返回写入成功,唤醒等待线程mcr.setDiskWriteResult(true,true);if(DEBUG){Log.d(TAG,"write: "+(existsTime-startTime)+"/"+(backupExistsTime-startTime)+"/"+(outputStreamCreateTime-startTime)+"/"+(writeTime-startTime)+"/"+(fsyncTime-startTime)+"/"+(setPermTime-startTime)+"/"+(fstatTime-startTime)+"/"+(deleteTime-startTime));}longfsyncDuration=fsyncTime-writeTime;mSyncTimes.add((int)fsyncDuration);mNumSync++;if(DEBUG||mNumSync%1024==0||fsyncDuration>MAX_FSYNC_DURATION_MILLIS){mSyncTimes.log(TAG,"Time required to fsync "+mFile+": ");}return;}catch(XmlPullParserExceptione){Log.w(TAG,"writeToFile: Got exception:",e);}catch(IOExceptione){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);}
@Overridepublicvoidapply(){finallongstartTime=System.currentTimeMillis();finalMemoryCommitResultmcr=commitToMemory();finalRunnableawaitCommit=newRunnable(){@Overridepublicvoidrun(){try{//进入等待状态mcr.writtenToDiskLatch.await();}catch(InterruptedExceptionignored){}if(DEBUG&&mcr.wasWritten){Log.d(TAG,mFile.getName()+":"+mcr.memoryStateGeneration+" applied after "+(System.currentTimeMillis()-startTime)+" ms");}}};QueuedWork.addFinisher(awaitCommit);RunnablepostWriteRunnable=newRunnable(){@Overridepublicvoidrun(){awaitCommit.run();QueuedWork.removeFinisher(awaitCommit);}};SharedPreferencesImpl.this.enqueueDiskWrite(mcr,postWriteRunnable);// 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);}