//frameworks/base/core/java/android/app/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){//获取路径file=getSharedPreferencesPath(name);mSharedPrefsPaths.put(name,file);}}//调用重载方法getSharedPreferencesreturngetSharedPreferences(file,mode);}
@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());}}
//frameworks/base/core/java/android/app/ContextImpl.javaprivatestaticFileensurePrivateDirExists(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();}}}
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);}}
@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();}
@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);}
privatevoidwriteToFile(MemoryCommitResultmcr,booleanisFromSyncCommit){longstartTime=0;longexistsTime=0;longbackupExistsTime=0;longoutputStreamCreateTime=0;longwriteTime=0;longfsyncTime=0;longsetPermTime=0;longfstatTime=0;longdeleteTime=0;booleanfileExists=mFile.exists();// 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(!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(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);try{finalStructStatstat=Os.stat(mFile.getPath());synchronized(mLock){mStatTimestamp=stat.st_mtim;mStatSize=stat.st_size;}}catch(ErrnoExceptione){// Do nothing}// 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);longfsyncDuration=fsyncTime-writeTime;mSyncTimes.add((int)fsyncDuration);mNumSync++;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);}