Android文件存儲(chǔ)SharedPreferences源碼解析
1.我們都知道SharedPreferences 是android可以用來存放key value的的文件。
SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("key","value");
editor.commit(); SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("key","value");
editor.apply();SharedPreferences是一個(gè)接口。getSharedPreferences 拿到的是它的實(shí)現(xiàn)類SharedPreferencesImpl。
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
public SharedPreferences getSharedPreferences(String name, int mode) {
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.put(name, sp);
return sp;
}
}在構(gòu)造函數(shù)中,會(huì)把存儲(chǔ)的鍵值對(duì)保存到一個(gè)hashMap中
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
//讀取文件中存儲(chǔ)的key value,并存到全局變量mMap中
startLoadFromDisk();
} private void loadFromDiskLocked() {
.......
str = new BufferedInputStream(
new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<String, Object>();
}
}當(dāng)我們getString等取值的時(shí)候,就是從這個(gè)mMap中取的。
get方法就是從這個(gè)map中讀取。
public String getString(String key, String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}2. sharedPrefrence存數(shù)據(jù),有兩種方式,commit和apply。
sp.edit()拿到的也是一個(gè)接口,Editor,實(shí)現(xiàn)類是EditorImpl。
SharedPreferences.Editor editor = sp.edit();
public Editor edit() {
return new EditorImpl();
}當(dāng)調(diào)用putString(String key, String value)時(shí),先保存到了一個(gè)map中
private final Map<String, Object> mModified = Maps.newHashMap();
public Editor putString(String key, String value) {
synchronized (this) {
//將要修改的key value,存放到map中
mModified.put(key, value);
return this;
}
}那么commit和apply的區(qū)別是什么?
1).commit有返回值是一個(gè)boolean類型。
apply沒有返回值,返回的是void。
2)commit是同步存儲(chǔ),所以必須拿到返回值,代碼才能往下走,否則會(huì)阻塞在這。
apply是異步存儲(chǔ),直接丟在了一個(gè)線程中執(zhí)行,我們不必等待他的返回結(jié)果。
直接看源碼
public boolean commit() {
MemoryCommitResult mcr = commitToMemory();
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}public void apply() {
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
QueuedWork.add(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
public void run() {
awaitCommit.run();
QueuedWork.remove(awaitCommit);
}
};
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
notifyListeners(mcr);
}分析源碼
commit和apply都調(diào)用了這行代碼,
final MemoryCommitResult mcr = commitToMemory();
和private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) ;
這倆的不同在于第二個(gè)參數(shù) Runnable postWriteRunnable。commit傳的是一個(gè)null,而apply傳的是一個(gè)Runnable對(duì)象。這個(gè)參數(shù)很關(guān)鍵,后面會(huì)根據(jù)這個(gè)參數(shù)進(jìn)行判斷,選擇是異步存儲(chǔ)還是同步存儲(chǔ)。
先看commitToMemory()是如何實(shí)現(xiàn)的。
這個(gè)方法是將要修改的鍵值對(duì)(存在mModified中),和文件中的的全量鍵值對(duì)(存在mMap中),
進(jìn)行比對(duì),把更新后的map賦值給 mcr.mapToWriteToDisk = mMap;
private MemoryCommitResult commitToMemory() {
MemoryCommitResult mcr = new MemoryCommitResult();
//mMap存儲(chǔ)了文件中所有的鍵值對(duì)。
mcr.mapToWriteToDisk = mMap;
對(duì)要新增或修改的鍵值對(duì)進(jìn)行遍歷。并添加到mMap中
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
if (mMap.containsKey(k)) {
Object existingValue = mMap.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mMap.put(k, v);
}
mcr.changesMade = true;
mModified.clear();
return mcr;
}在看第二個(gè)方法enqueueDiskWrite(mrc,runnable)。
如果是commit方式存儲(chǔ),runnable==null。則調(diào)用writeToDiskRunnable.run();進(jìn)行存儲(chǔ),這個(gè)方法是同步的。
如果是apply方式存儲(chǔ),runnable!=null。會(huì)直接放進(jìn)一個(gè)線程池中執(zhí)行。
QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
這也就是為什么apply是異步存儲(chǔ)。
注意第二個(gè)參數(shù),commit傳的是null。apply傳的是一個(gè)postWriteRunnable
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
final Runnable writeToDiskRunnable = new Runnable() {
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr);
}
synchronized (SharedPreferencesImpl.this) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
//根據(jù) postWriteRunnable 是不是null來區(qū)分是commit方式還是apply方式
final boolean isFromSyncCommit = (postWriteRunnable == null);
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
//如果是commit方式,上面的注釋很也說明了commit是在當(dāng)前線程執(zhí)行的文件存儲(chǔ)。
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (SharedPreferencesImpl.this) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
//直接調(diào)用Runnable的run方法。在當(dāng)前線程執(zhí)行文件的存儲(chǔ)。所以是同步方式
writeToDiskRunnable.run();
return;
}
}
// 如果是applay方式,上面代碼不會(huì)執(zhí)行,也就不會(huì)return。
//則會(huì)把存儲(chǔ)文件的方法放到一個(gè)線程池中去執(zhí)行
QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
}然后在看看writeToFile(MemoryCommitResult mcr)。將修改后的鍵值對(duì),保存入文件中。
先是對(duì)源文件做了一個(gè)備份,然后全量的寫入文件。
如果寫成功,會(huì)將備份文件刪除。
如果寫文件時(shí)出現(xiàn)異常,則會(huì)將備份文件恢復(fù)。
private void writeToFile(MemoryCommitResult mcr) {
//在寫文件前,先將源文件進(jìn)行一個(gè)備份
if (!mBackupFile.exists()) {
if (!mFile.renameTo(mBackupFile)) {
mcr.setDiskWriteResult(false);
return;
}
} else { //如果備份文件存在,則將源文件刪掉
mFile.delete();
}
FileOutputStream str = createFileOutputStream(mFile);
//將文件中所有的keyvalue,保護(hù)要修改的,全量存入新的文件中。
XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
FileUtils.sync(str);
str.close();
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
// Writing was successful, delete the backup file if there is one.
//刪除備份的文件
mBackupFile.delete();
mcr.setDiskWriteResult(true);
}到此這篇關(guān)于Android文件存儲(chǔ)SharedPreferences源碼解析的文章就介紹到這了,更多相關(guān)Android SharedPreferences內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android SharedPreferences數(shù)據(jù)存儲(chǔ)詳解
- Android用SharedPreferences實(shí)現(xiàn)登錄注冊(cè)注銷功能
- 使用SharedPreferences在Android存儲(chǔ)對(duì)象詳細(xì)代碼
- Android SharedPreferences存取操作以及封裝詳解
- Android 文件存儲(chǔ)與SharedPreferences存儲(chǔ)方式詳解用法
- Android 使用 SharedPreferences 保存少量數(shù)據(jù)的實(shí)現(xiàn)代碼
- Android SharedPreference存儲(chǔ)文件三步走
相關(guān)文章
詳解Flutter WebView與JS互相調(diào)用簡(jiǎn)易指南
這篇文章主要介紹了詳解Flutter WebView與JS互相調(diào)用簡(jiǎn)易指南,分為JS調(diào)用Flutter和Flutter調(diào)用JS,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-04-04
Android ViewPager畫廊效果詳解及實(shí)例
這篇文章主要介紹了Android ViewPager畫廊效果詳解及實(shí)例的相關(guān)資料,這里提供實(shí)例代碼及實(shí)現(xiàn)效果圖,具有參考價(jià)值,需要的朋友可以參考下2016-12-12
詳解Android的自動(dòng)化構(gòu)建及發(fā)布
本篇文章主要介紹了Android的自動(dòng)化構(gòu)建及發(fā)布,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
Android中EditText 設(shè)置 imeOptions 無效問題的解決方法
有時(shí)候我們需要在EditText 輸出完之后 需要在鍵盤出現(xiàn) 右下角變成“Go”或“前往 搜索時(shí);通常我們需要設(shè)置Android:imeOptions屬性,但是今天我發(fā)現(xiàn)設(shè)置了無效,下面給大家分享下解決方案2016-12-12
Android實(shí)現(xiàn)長(zhǎng)圖展開與收起效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)長(zhǎng)圖展開與收起效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09
從源碼編譯Android系統(tǒng)的Java類庫和JNI動(dòng)態(tài)庫的方法
這篇文章主要介紹了從源碼編譯Android系統(tǒng)的Java類庫和JNI動(dòng)態(tài)庫的方法,例子基于Linux系統(tǒng)環(huán)境下來講,需要的朋友可以參考下2016-02-02
Flutter 狀態(tài)管理的實(shí)現(xiàn)
這篇文章主要介紹了Flutter 狀態(tài)管理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Android封裝MVP實(shí)現(xiàn)登錄注冊(cè)功能
這篇文章主要為大家詳細(xì)介紹了Android封裝MVP實(shí)現(xiàn)登錄注冊(cè)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11

