欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SharedPreference 初始化源碼解析

 更新時(shí)間:2023年03月30日 09:12:38   作者:海象  
這篇文章主要為大家介紹了SharedPreference 初始化源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

初始化

sp 內(nèi)部將數(shù)據(jù)放到 xml 文件中,加載時(shí)首先會(huì)將硬盤(pán)中文件讀取到內(nèi)存中,這樣加快了訪問(wèn)速度

這次從源碼開(kāi)始,看看里面具體做了什么

    // 初始化
    SharedPreferencesImpl(File file, int mode) {
        // 文件
        mFile = file;
        //備份文件 .bak 結(jié)尾,看看什么時(shí)候排上作用,比如恢復(fù)數(shù)據(jù)
        mBackupFile = makeBackupFile(file);
        mMode = mode;
        mLoaded = false;
        mMap = null;
        mThrowable = null;
        // 從硬盤(pán)中讀取
        startLoadFromDisk();
    }

硬盤(pán)中讀取文件開(kāi)了新線程,主要將文件中的內(nèi)容,轉(zhuǎn)換為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);
                    // 轉(zhuǎn)為 map            
                    map = (Map<String, Object>) XmlUtils.readMapXml(str);
                } catch (Exception e) {
                    Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
                } finally {
                    // 關(guān)閉流
                    IoUtils.closeQuietly(str);
                }
    }

流程很簡(jiǎn)單,就是讀取硬盤(pán),轉(zhuǎn)換為一個(gè) Map

apply,commit 區(qū)別

首先 apply,commit 分別是異步/同步的寫(xiě)入操作,它們都會(huì)先寫(xiě)入內(nèi)存中,也就是更新 Map,不同在于寫(xiě)入到硬盤(pán)的時(shí)機(jī)不同

  • commit 先看 commit 做了什么 ,commit 方法將返回一個(gè)布爾值,表示結(jié)果
        @Override
        public boolean commit() {
            // 先提交到內(nèi)存中
            MemoryCommitResult mcr = commitToMemory();
            // 執(zhí)行硬盤(pán)中的更新
            SharedPreferencesImpl.this.enqueueDiskWrite(
                mcr, null /* sync write on this thread okay */);
            try {
                mcr.writtenToDiskLatch.await();
            } catch (InterruptedException e) {
                // 提交異常,返回 false
                return false;
            }
            // 通知監(jiān)聽(tīng)
            notifyListeners(mcr);
            // 返回結(jié)果
            return mcr.writeToDiskResult;
        }
  • apply
        @Override
        public void apply() {
            final long startTime = System.currentTimeMillis();
            // 都是一樣的,先寫(xiě)到內(nèi)存
            final MemoryCommitResult mcr = commitToMemory();
            final Runnable awaitCommit = new Runnable() {
                    @Override
                    public void run() {
                        // 
                        mcr.writtenToDiskLatch.await();
                    }
                };
            // 往 sFinishers 隊(duì)列中添加,等待執(zhí)行
            QueuedWork.addFinisher(awaitCommit);
            // 在寫(xiě)完后執(zhí)行 postWriteRunnable
            Runnable postWriteRunnable = new Runnable() {
                    @Override
                    public void run() {
                        // 執(zhí)行 awaitCommit
                        awaitCommit.run();
                        // sFinishers 隊(duì)列中移除
                        QueuedWork.removeFinisher(awaitCommit);
                    }
                };
            // 寫(xiě)入硬盤(pán)
            SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
        }

硬盤(pán)中是如何更新的呢

    private void enqueueDiskWrite(final MemoryCommitResult mcr,
                                  final Runnable postWriteRunnable) {
        final boolean isFromSyncCommit = (postWriteRunnable == null);
        // 創(chuàng)建 Runnable 對(duì)象
        final Runnable writeToDiskRunnable = new Runnable() {
                @Override
                public void run() {
                    // 寫(xiě)到文件,這里的鎖是 mWritingToDiskLock 對(duì)象
                    synchronized (mWritingToDiskLock) {
                        writeToFile(mcr, isFromSyncCommit);
                    }
                    synchronized (mLock) {
                        mDiskWritesInFlight--;
                    }
                    // 執(zhí)行 postWriteRunnable, commit 這里為 null
                    // apply 時(shí)不為i而空
                    if (postWriteRunnable != null) {
                        postWriteRunnable.run();
                    }
                }
            };
        // Typical #commit() path with fewer allocations, doing a write on
        // the current thread.
        // 是否為同步提交
        // 根據(jù) postWriteRunnable 是否為空, commit 這里為 true
        // apply 
        if (isFromSyncCommit) {
            boolean wasEmpty = false;
            synchronized (mLock) {
                wasEmpty = mDiskWritesInFlight == 1;
            }
            if (wasEmpty) {
                writeToDiskRunnable.run();
                return;
            }
        }
        // 放到隊(duì)列中執(zhí)行,內(nèi)部是一個(gè) HandlerThread,按照隊(duì)列逐個(gè)執(zhí)行任務(wù)
        QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
    }

這里用隊(duì)列來(lái)放任務(wù),應(yīng)該是要應(yīng)對(duì)多個(gè) commit 情況,這里將所有 commit 往隊(duì)列里面放,放完后就會(huì)執(zhí)行硬盤(pán)的寫(xiě),apply 也會(huì)調(diào)用到這里

   public static void queue(Runnable work, boolean shouldDelay) {
        Handler handler = getHandler();
        synchronized (sLock) {
            // 添加到 sWork 隊(duì)列中
            sWork.add(work);
            // 異步 apply 走這個(gè)
            if (shouldDelay && sCanDelay) {
                handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
            } else {
            // 同步 commit 走這個(gè)
                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
            }
        }
    }

apply 的硬盤(pán)寫(xiě)入,需要等待 Activity.onPause() 等時(shí)機(jī)才會(huì)執(zhí)行

讀取

讀取比寫(xiě)入就簡(jiǎn)單很多了

  • 先查看是否從硬盤(pán)加載到了內(nèi)存,沒(méi)有就先去加載
  • 從內(nèi)存中讀取
 public String getString(String key, @Nullable String defValue) {
        synchronized (mLock) {
            // 檢查是否從硬盤(pán)加載到了內(nèi)存,沒(méi)有就先去加載
            awaitLoadedLocked();
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
    }

如何保證線程安全的

通過(guò) sync 加對(duì)象鎖,內(nèi)存讀寫(xiě)都是用的同一把鎖,所以讀寫(xiě)都是線程安全的

數(shù)據(jù)恢復(fù)

存在備份機(jī)制

  • 對(duì)文件進(jìn)行寫(xiě)入操作,寫(xiě)入成功時(shí),則將備份文件刪除
  • 如果寫(xiě)入失敗,之后重新初始化時(shí),就使用備份文件恢復(fù)

SP 與 ANR

由于 Activity.onPause 會(huì)執(zhí)行 apply 的數(shù)據(jù)落盤(pán),里面是有等待鎖的,如果時(shí)間太長(zhǎng)就會(huì) ANR

小結(jié)

從 sp 的初始化,讀寫(xiě)方面簡(jiǎn)單分析了流程,sp 有數(shù)據(jù)恢復(fù)機(jī)制這是 mmkv 所欠缺的,但在多進(jìn)程環(huán)境下,sp 是不可靠的

相關(guān)鏈接

以上就是SharedPreference 初始化源碼解析的詳細(xì)內(nèi)容,更多關(guān)于SharedPreference 初始化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論