源碼學(xué)習(xí)->18SharedPreferences

一兄旬、SharedPreferences構(gòu)建:

1.1 ContextImpl.getSharedPreferences:

private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;

@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
    SharedPreferencesImpl sp;
    synchronized (ContextImpl.class) {
        if (sSharedPrefs == null) {
            sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
        }

        final String packageName = getPackageName();
        ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
        if (packagePrefs == null) {
            packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
            sSharedPrefs.put(packageName, packagePrefs);
        }
        /**
         * api19以下支持name = null;
         */
        if (mPackageInfo.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) {
            if (name == null) {
                name = "null";
            }
        }

        sp = packagePrefs.get(name);
        if (sp == null) {
            /**
             * 1. 一個name對應(yīng)一個SharedPreferencesImpl;
             * 2. 將File緩存在SharedPreferencesImpl中;
             */
            File prefsFile = getSharedPrefsFile(name);     模塊<1.2>
            sp = new SharedPreferencesImpl(prefsFile, mode);     模塊<1.3>
            packagePrefs.put(name, sp);
            return sp;
        }
    }
    return sp;
}
1.2 ContextImpl.getSharedPrefsFile:
/**
 * 構(gòu)建對應(yīng)的File文件;
 */
@Override
public File getSharedPrefsFile(String name) {
    return makeFilename(getPreferencesDir(), name + ".xml");
}

private File makeFilename(File base, String name) {
    if (name.indexOf(File.separatorChar) < 0) {
        return new File(base, name);
    }
}
1.3 SharedPreferencesImpl構(gòu)造函數(shù):
SharedPreferencesImpl(File file, int mode) {
    mFile = file;
    mBackupFile = makeBackupFile(file);
    mMode = mode;
    mLoaded = false;
    mMap = null;
    /**
     * 1. 結(jié)合模塊<1.4>可知, 在使用Sp時, 需要先根據(jù)name將指定目錄下的<name>文件中的內(nèi)容全部
     *    加載進(jìn)內(nèi)存中, 然后在進(jìn)行讀寫操作;
     * 2. 數(shù)據(jù)量越大, 加載就越耗時, 所以這也就是為什么一直強(qiáng)調(diào)盡量不要在一個sp中存儲大量數(shù)據(jù);
     */
    startLoadFromDisk();    模塊<1.4>
}
1.4 SharedPreferencesImpl.startLoadFromDisk:
private void startLoadFromDisk() {
    synchronized (this) {
        mLoaded = false;
    }
    new Thread("SharedPreferencesImpl-load") {
        public void run() {
            synchronized (SharedPreferencesImpl.this) {
                /**
                 * File中的數(shù)據(jù)加載進(jìn)Map是在子線程中進(jìn)行, 與putXXX操作如何進(jìn)行同步?  模塊<二>
                 */
                loadFromDiskLocked();
            }
        }
    }.start();
}

private void loadFromDiskLocked() {
    if (mLoaded) {
        return;
    }
    if (mBackupFile.exists()) {
        mFile.delete();
        mBackupFile.renameTo(mFile);
    }
    Map map = null;
    StructStat stat = null;
    stat = Os.stat(mFile.getPath());
    if (mFile.canRead()) {
        /**
         * 數(shù)據(jù)量越大, 這一塊就越耗時, 進(jìn)行g(shù)et/edit操作時如果加載沒完成會處于阻塞狀態(tài);
         */
        BufferedInputStream str = null;
        str = new BufferedInputStream(new FileInputStream(mFile), 16*1024);
        map = XmlUtils.readMapXml(str);
    }
    mLoaded = true;
    if (map != null) {
        mMap = map;
        mStatTimestamp = stat.st_mtime;
        mStatSize = stat.st_size;
    } else {
        mMap = new HashMap<String, Object>();
    }
    notifyAll();
}

二、SharedPreferencesImpl.edit:

public Editor edit() {
    /**
     * 這里的插入操作與初始化Sp時磁盤數(shù)據(jù)加載到內(nèi)存共用同一把鎖, 如果初始化時的數(shù)據(jù)加載
     * 耗時過長, 這里就會被一直阻塞;
     */
    synchronized (this) {
        awaitLoadedLocked();
    }
    return new EditorImpl();
}

private void awaitLoadedLocked() {
    while (!mLoaded) {
        wait();
    }
}

三浑侥、EditorImpl.putXXX:

public Editor putInt(String key, int value) {
    synchronized (this) {
        /**
         * 對于指定name的Sp, 由于sp與EditorImpl都是單例, 在結(jié)合這里的鎖對象, 所以Sp是線程安全的;
         */
        mModified.put(key, value);
        return this;
    }
}

四枫耳、EditorImpl.commit:

4.1 EditorImpl.commit:
public boolean commit() {
    /**
     * 將數(shù)據(jù)從緩存集合mModified讀取到mMap中, 然后將mMap賦值給MCR;
     */
    MemoryCommitResult mcr = commitToMemory();     模塊<4.2>
    /**
     * 將數(shù)據(jù)從mcr.mapToWriteToDisk中寫入到磁盤中;
     */
    SharedPreferencesImpl.this.enqueueDiskWrite(mcr, null);     模塊<4.3>
    try {
        mcr.writtenToDiskLatch.await();   
    } catch (InterruptedException e) {
        return false;
    }
    notifyListeners(mcr);
    return mcr.writeToDiskResult;
}
4.2 EditorImpl.commitToMemory:
private MemoryCommitResult commitToMemory() {
    MemoryCommitResult mcr = new MemoryCommitResult();
    synchronized (SharedPreferencesImpl.this) {
        if (mDiskWritesInFlight > 0) {
            mMap = new HashMap<String, Object>(mMap);
        }
        mcr.mapToWriteToDisk = mMap;
        mDiskWritesInFlight++;
        boolean hasListeners = mListeners.size() > 0;
        if (hasListeners) {
            mcr.keysModified = new ArrayList<String>();
            mcr.listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
        }
        synchronized (this) {
            for (Map.Entry<String, Object> e : mModified.entrySet()) {
                String k = e.getKey();
                Object v = e.getValue();
                ...
                mMap.put(k, v);
                mcr.changesMade = true;
                if (hasListeners) {
                    mcr.keysModified.add(k);
                }
            }
            mModified.clear();
        }
    }
    return mcr;
}
  • 簡而言之, EditorImpl.putXXX方法將數(shù)據(jù)緩存進(jìn)mModified中, 然后調(diào)用commit將mModified數(shù)據(jù)讀取到mMap中, 然后賦值給MemoryCommitResult.mapToWriteToDisk, 同時情況mModified;
4.3 SharedPreferencesImpl.enqueueDiskWrite:
private void enqueueDiskWrite(final MemoryCommitResult mcr, final Runnable postWriteRunnable) {
    final Runnable writeToDiskRunnable = new Runnable() {
        public void run() {
            // 全局共用一把鎖, 寫入磁盤操作是線程同步的;
            synchronized (mWritingToDiskLock) {
                writeToFile(mcr);       模塊<4.4>
            }
            synchronized (SharedPreferencesImpl.this) {
                mDiskWritesInFlight--;
            }
            if (postWriteRunnable != null) {
                postWriteRunnable.run();
            }
        }
    };
    /**
     * commit方式 ---> isFromSyncCommit = true;
     * apply方式 ---> isFromSyncCommit = false;
     */
    final boolean isFromSyncCommit = (postWriteRunnable == null);

    if (isFromSyncCommit) {
        boolean wasEmpty = false;
        synchronized (SharedPreferencesImpl.this) {
            /**
             * 1. 結(jié)合模塊<4.2>可知, mDiskWritesInFlight默認(rèn)為0, 調(diào)用一次commit, 觸發(fā)一次
             *    mDiskWritesInFlight++ = 1操作, 然后在writeToDiskRunnable.run中又重置
             *    mDiskWritesInFlight = 0操作;
             * 2. 所以調(diào)用一次commit時, mDiskWritesInFlight == 1, empty = true;
             */
            wasEmpty = mDiskWritesInFlight == 1;
        }
        if (wasEmpty) {
            /**
             * 調(diào)用commit時會跳轉(zhuǎn)到這里, 觸發(fā)writeToDiskRunnable.run;
             */
            writeToDiskRunnable.run();
            return;
        }
    }
    /**
     * 如果是apply方式, 則會執(zhí)行到這里, 可知, writeToDiskRunnable被運行在子線程中, 然后
     * 觸發(fā)writeToFile在子線程中執(zhí)行;
     */
    QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);    模塊<5.2>
}
4.4 SharedPreferencesImpl.writeToFile:
private void writeToFile(MemoryCommitResult mcr) {
    if (mFile.exists()) {
        if (!mcr.changesMade) {
            /**
             * 正如文章所說, changesMade默認(rèn)為false, mModified數(shù)據(jù)被寫入到mMap時將changesMade
             * 置為true, 所以如果數(shù)據(jù)沒有發(fā)生變化, 則不對File做任何處理;
             */
            mcr.setDiskWriteResult(true);
            return;
        }
    }
    try {
        FileOutputStream str = createFileOutputStream(mFile);
        if (str == null) {
            mcr.setDiskWriteResult(false);
            return;
        }
        XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
        FileUtils.sync(str);
        str.close();
        /**
         * 設(shè)置文件的讀寫權(quán)限;
         */
        ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
        final StructStat stat = Os.stat(mFile.getPath());
        synchronized (this) {
            mStatTimestamp = stat.st_mtime;
            mStatSize = stat.st_size;
        }
        mBackupFile.delete();
        mcr.setDiskWriteResult(true);
        return;
    } 
    catch (XmlPullParserException e) {...} 
    catch (IOException e) {...}
    mcr.setDiskWriteResult(false);
}
五、EditorImpl.apply:
5.1 EditorImpl.apply:
public void apply() {
    /**
     * 將mModified數(shù)據(jù)寫入到MemoryCommitResult中;
     */
    final MemoryCommitResult mcr = commitToMemory();
    final Runnable awaitCommit = new Runnable() {
        public void run() {
            mcr.writtenToDiskLatch.await();
        }
    };
    QueuedWork.add(awaitCommit);
    Runnable postWriteRunnable = new Runnable() {
        public void run() {
            awaitCommit.run();
            QueuedWork.remove(awaitCommit);
        }
    };
    /**
     * 將MemoryCommitResult中緩存的數(shù)據(jù)寫入到磁盤中;
     */
    SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);    模塊<4.3>
    notifyListeners(mcr);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泳挥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌至朗,老刑警劉巖屉符,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锹引,居然都是意外死亡矗钟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門嫌变,熙熙樓的掌柜王于貴愁眉苦臉地迎上來真仲,“玉大人,你說我怎么就攤上這事初澎。” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵碑宴,是天一觀的道長软啼。 經(jīng)常有香客問我,道長延柠,這世上最難降的妖魔是什么祸挪? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮贞间,結(jié)果婚禮上贿条,老公的妹妹穿的比我還像新娘。我一直安慰自己增热,他們只是感情好整以,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峻仇,像睡著了一般公黑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上摄咆,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天凡蚜,我揣著相機(jī)與錄音,去河邊找鬼吭从。 笑死朝蜘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涩金。 我是一名探鬼主播谱醇,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸭廷!你這毒婦竟也來了枣抱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辆床,失蹤者是張志新(化名)和其女友劉穎佳晶,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讼载,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡轿秧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了咨堤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菇篡。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖一喘,靈堂內(nèi)的尸體忽然破棺而出驱还,到底是詐尸還是另有隱情嗜暴,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布议蟆,位于F島的核電站闷沥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咐容。R本人自食惡果不足惜舆逃,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望戳粒。 院中可真熱鬧路狮,春花似錦、人聲如沸蔚约。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炊琉。三九已至展蒂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苔咪,已是汗流浹背锰悼。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留团赏,地道東北人箕般。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像舔清,于是被迫代替她去往敵國和親丝里。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容