您好,登錄后才能下訂單哦!
本篇內容介紹了“SharedPreference初始化源碼分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
sp 內部將數據放到 xml 文件中,加載時首先會將硬盤中文件讀取到內存中,這樣加快了訪問速度
這次從源碼開始,看看里面具體做了什么
// 初始化 SharedPreferencesImpl(File file, int mode) { // 文件 mFile = file; //備份文件 .bak 結尾,看看什么時候排上作用,比如恢復數據 mBackupFile = makeBackupFile(file); mMode = mode; mLoaded = false; mMap = null; mThrowable = null; // 從硬盤中讀取 startLoadFromDisk(); }
硬盤中讀取文件開了新線程,主要將文件中的內容,轉換為Map
private void loadFromDisk() { synchronized (mLock) { if (mLoaded) { return; } // 存在備份文件,刪除 file,為什么 if (mBackupFile.exists()) { mFile.delete(); mBackupFile.renameTo(mFile); } } Map<String, Object> map = null; StructStat stat = null; Throwable thrown = null; stat = Os.stat(mFile.getPath()); // 讀取流 BufferedInputStream str = null; try { str = new BufferedInputStream( new FileInputStream(mFile), 16 * 1024); // 轉為 map map = (Map<String, Object>) XmlUtils.readMapXml(str); } catch (Exception e) { Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e); } finally { // 關閉流 IoUtils.closeQuietly(str); } }
流程很簡單,就是讀取硬盤,轉換為一個 Map
首先 apply,commit 分別是異步/同步的寫入操作,它們都會先寫入內存中,也就是更新 Map,不同在于寫入到硬盤的時機不同
commit 先看 commit 做了什么 ,commit 方法將返回一個布爾值,表示結果
@Override public boolean commit() { // 先提交到內存中 MemoryCommitResult mcr = commitToMemory(); // 執行硬盤中的更新 SharedPreferencesImpl.this.enqueueDiskWrite( mcr, null /* sync write on this thread okay */); try { mcr.writtenToDiskLatch.await(); } catch (InterruptedException e) { // 提交異常,返回 false return false; } // 通知監聽 notifyListeners(mcr); // 返回結果 return mcr.writeToDiskResult; }
apply
@Override public void apply() { final long startTime = System.currentTimeMillis(); // 都是一樣的,先寫到內存 final MemoryCommitResult mcr = commitToMemory(); final Runnable awaitCommit = new Runnable() { @Override public void run() { // mcr.writtenToDiskLatch.await(); } }; // 往 sFinishers 隊列中添加,等待執行 QueuedWork.addFinisher(awaitCommit); // 在寫完后執行 postWriteRunnable Runnable postWriteRunnable = new Runnable() { @Override public void run() { // 執行 awaitCommit awaitCommit.run(); // sFinishers 隊列中移除 QueuedWork.removeFinisher(awaitCommit); } }; // 寫入硬盤 SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable); }
硬盤中是如何更新的呢
private void enqueueDiskWrite(final MemoryCommitResult mcr, final Runnable postWriteRunnable) { final boolean isFromSyncCommit = (postWriteRunnable == null); // 創建 Runnable 對象 final Runnable writeToDiskRunnable = new Runnable() { @Override public void run() { // 寫到文件,這里的鎖是 mWritingToDiskLock 對象 synchronized (mWritingToDiskLock) { writeToFile(mcr, isFromSyncCommit); } synchronized (mLock) { mDiskWritesInFlight--; } // 執行 postWriteRunnable, commit 這里為 null // apply 時不為i而空 if (postWriteRunnable != null) { postWriteRunnable.run(); } } }; // Typical #commit() path with fewer allocations, doing a write on // the current thread. // 是否為同步提交 // 根據 postWriteRunnable 是否為空, commit 這里為 true // apply if (isFromSyncCommit) { boolean wasEmpty = false; synchronized (mLock) { wasEmpty = mDiskWritesInFlight == 1; } if (wasEmpty) { writeToDiskRunnable.run(); return; } } // 放到隊列中執行,內部是一個 HandlerThread,按照隊列逐個執行任務 QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit); }
這里用隊列來放任務,應該是要應對多個 commit 情況,這里將所有 commit 往隊列里面放,放完后就會執行硬盤的寫,apply 也會調用到這里
public static void queue(Runnable work, boolean shouldDelay) { Handler handler = getHandler(); synchronized (sLock) { // 添加到 sWork 隊列中 sWork.add(work); // 異步 apply 走這個 if (shouldDelay && sCanDelay) { handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY); } else { // 同步 commit 走這個 handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN); } } }
apply 的硬盤寫入,需要等待 Activity.onPause() 等時機才會執行
讀取比寫入就簡單很多了
先查看是否從硬盤加載到了內存,沒有就先去加載
從內存中讀取
public String getString(String key, @Nullable String defValue) { synchronized (mLock) { // 檢查是否從硬盤加載到了內存,沒有就先去加載 awaitLoadedLocked(); String v = (String)mMap.get(key); return v != null ? v : defValue; } }
通過 sync 加對象鎖,內存讀寫都是用的同一把鎖,所以讀寫都是線程安全的
存在備份機制
對文件進行寫入操作,寫入成功時,則將備份文件刪除
如果寫入失敗,之后重新初始化時,就使用備份文件恢復
由于 Activity.onPause 會執行 apply 的數據落盤,里面是有等待鎖的,如果時間太長就會 ANR
“SharedPreference初始化源碼分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。