@OverridepublicSharedPreferencesgetSharedPreferences(Filefile,intmode){SharedPreferencesImplsp;synchronized(ContextImpl.class){// 缓存finalArrayMap<File,SharedPreferencesImpl>cache=getSharedPreferencesCacheLocked();sp=cache.get(file);if(sp==null){// 检查mode,不允许MODE_WORLD_READABLE或MODE_WORLD_WRITEABLEcheckMode(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");}}sp=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){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");}}}
@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);}}
@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();}
privateMemoryCommitResultcommitToMemory(){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;}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++;}memoryStateGeneration=mCurrentMemoryStateGeneration;}}returnnewMemoryCommitResult(memoryStateGeneration,keysCleared,keysModified,listeners,mapToWriteToDisk);}
/**
* Queue a work-runnable for processing asynchronously.
*
* @param work The new runnable to process
* @param shouldDelay If the message should be delayed
*/@UnsupportedAppUsage(maxTargetSdk=Build.VERSION_CODES.R,trackingBug=170729553)publicstaticvoidqueue(Runnablework,booleanshouldDelay){// 返回的对象是一个handler,创建对象的参数Loop是handlerThread创建的子线程的。Handlerhandler=getHandler();synchronized(sLock){// 加到链表sWork.add(work);// 发送消息,sCanDelay留坑if(shouldDelay&&sCanDelay){handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN,DELAY);}else{// commit一定走这里handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);}}}
privatestaticvoidprocessPendingWork(){longstartTime=0;if(DEBUG){startTime=System.currentTimeMillis();}synchronized(sProcessingWork){LinkedList<Runnable>work;synchronized(sLock){work=sWork;sWork=newLinkedList<>();// Remove all msg-s as all work will be processed nowgetHandler().removeMessages(QueuedWorkHandler.MSG_RUN);}if(work.size()>0){for(Runnablew:work){// 虽然是调用run,运行确实在子线程。w.run();}if(DEBUG){Log.d(LOG_TAG,"processing "+work.size()+" items took "++(System.currentTimeMillis()-startTime)+" ms");}}}}
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);}};
/**
* 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)
*/