網站首頁 編程語言 正文
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是一個接口。getSharedPreferences 拿到的是它的實現類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;
}
}
在構造函數中,會把存儲的鍵值對保存到一個hashMap中
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
//讀取文件中存儲的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>();
}
}
當我們getString等取值的時候,就是從這個mMap中取的。
get方法就是從這個map中讀取。
public String getString(String key, String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
2. sharedPrefrence存數據,有兩種方式,commit和apply。
sp.edit()拿到的也是一個接口,Editor,實現類是EditorImpl。
SharedPreferences.Editor editor = sp.edit();
public Editor edit() {
return new EditorImpl();
}
當調用putString(String key, String value)時,先保存到了一個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的區別是什么?
1).commit有返回值是一個boolean類型。
apply沒有返回值,返回的是void。
2)commit是同步存儲,所以必須拿到返回值,代碼才能往下走,否則會阻塞在這。
apply是異步存儲,直接丟在了一個線程中執行,我們不必等待他的返回結果。
直接看源碼
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都調用了這行代碼,
final MemoryCommitResult mcr = commitToMemory();
和private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) ;
這倆的不同在于第二個參數 Runnable postWriteRunnable。commit傳的是一個null,而apply傳的是一個Runnable對象。這個參數很關鍵,后面會根據這個參數進行判斷,選擇是異步存儲還是同步存儲。
先看commitToMemory()是如何實現的。
這個方法是將要修改的鍵值對(存在mModified中),和文件中的的全量鍵值對(存在mMap中),
進行比對,把更新后的map賦值給 mcr.mapToWriteToDisk = mMap;
private MemoryCommitResult commitToMemory() {
MemoryCommitResult mcr = new MemoryCommitResult();
//mMap存儲了文件中所有的鍵值對。
mcr.mapToWriteToDisk = mMap;
對要新增或修改的鍵值對進行遍歷。并添加到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;
}
在看第二個方法enqueueDiskWrite(mrc,runnable)。
如果是commit方式存儲,runnable==null。則調用writeToDiskRunnable.run();進行存儲,這個方法是同步的。
如果是apply方式存儲,runnable!=null。會直接放進一個線程池中執行。
QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
這也就是為什么apply是異步存儲。
注意第二個參數,commit傳的是null。apply傳的是一個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();
}
}
};
//根據 postWriteRunnable 是不是null來區分是commit方式還是apply方式
final boolean isFromSyncCommit = (postWriteRunnable == null);
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
//如果是commit方式,上面的注釋很也說明了commit是在當前線程執行的文件存儲。
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (SharedPreferencesImpl.this) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
//直接調用Runnable的run方法。在當前線程執行文件的存儲。所以是同步方式
writeToDiskRunnable.run();
return;
}
}
// 如果是applay方式,上面代碼不會執行,也就不會return。
//則會把存儲文件的方法放到一個線程池中去執行
QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
}
然后在看看writeToFile(MemoryCommitResult mcr)。將修改后的鍵值對,保存入文件中。
先是對源文件做了一個備份,然后全量的寫入文件。
如果寫成功,會將備份文件刪除。
如果寫文件時出現異常,則會將備份文件恢復。
private void writeToFile(MemoryCommitResult mcr) {
//在寫文件前,先將源文件進行一個備份
if (!mBackupFile.exists()) {
if (!mFile.renameTo(mBackupFile)) {
mcr.setDiskWriteResult(false);
return;
}
} else { //如果備份文件存在,則將源文件刪掉
mFile.delete();
}
FileOutputStream str = createFileOutputStream(mFile);
//將文件中所有的keyvalue,保護要修改的,全量存入新的文件中。
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);
}
原文鏈接:https://blog.csdn.net/niuyongzhi/article/details/126004472
相關推薦
- 2023-01-14 C/C++高精度(加減乘除)算法的實現_C 語言
- 2022-07-06 Qt之使用GraphicsView框架實現思維導圖的示例_C 語言
- 2023-02-12 完美解決Redis在雙擊redis-server.exe出現閃退問題_Redis
- 2022-12-04 C++?Boost.Range與Adapters庫使用詳解_C 語言
- 2023-01-03 python實現線性插值的示例_python
- 2022-04-20 詳解C語言讀取文件求某一列的平均值_C 語言
- 2024-03-15 Gitea Webhook報錯 webhook.ALLOWED_HOST_LIST setting
- 2022-03-20 C++靜態成員函數和this指針詳解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支