亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SharedPreference初始化源碼分析

發布時間:2023-03-31 17:02:39 來源:億速云 閱讀:82 作者:iii 欄目:開發技術

本篇內容介紹了“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 區別

首先 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 加對象鎖,內存讀寫都是用的同一把鎖,所以讀寫都是線程安全的

數據恢復

存在備份機制

  • 對文件進行寫入操作,寫入成功時,則將備份文件刪除

  • 如果寫入失敗,之后重新初始化時,就使用備份文件恢復

SP 與 ANR

由于 Activity.onPause 會執行 apply 的數據落盤,里面是有等待鎖的,如果時間太長就會 ANR

“SharedPreference初始化源碼分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

阿鲁科尔沁旗| 婺源县| 阳高县| 卢龙县| 昌图县| 左权县| 鹰潭市| 高青县| 镇宁| 西峡县| 淮安市| 永清县| 清水河县| 荃湾区| 新昌县| 林芝县| 涞源县| 大悟县| 鹤山市| 镇远县| 宁城县| 苏尼特左旗| 汝城县| 陕西省| 罗江县| 保亭| 页游| 武邑县| 大姚县| 临湘市| 桑日县| 江山市| 怀来县| 若尔盖县| 晋江市| 泰宁县| 永德县| 广东省| 屯留县| 名山县| 白沙|