<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
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); }
到此這篇關於Android檔案儲存SharedPreferences原始碼解析的文章就介紹到這了,更多相關Android SharedPreferences內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45