1.原理和概述
- 1.儲存于硬盤上的xml鍵值對。
- 2.輕量級數(shù)據(jù)儲存,數(shù)據(jù)多了容易引起性能問題
- 3.xml文件所在目錄位于/data/data//shared_prefs/,可以有多個文件。
2.初始化
ContextImpl記錄著SharedPreferences的重要數(shù)據(jù)
1.sSharedPrefsCache:以包名為key, 二級key是以SP文件, 以SharedPreferencesImpl為value的嵌套map結(jié)構(gòu). 這里需要sSharedPrefsCache是靜態(tài)類成員變量, 每個進程是保存唯一一份, 且由ContextImpl.class鎖保護.
2.mSharedPrefsPaths:記錄所有的SP文件, 以文件名為key, 具體文件為value的map結(jié)構(gòu)
3.mPreferencesDir:是指SP所在目錄, 是指/data/data//shared_prefs/Context.getSharedPreferences(name, mode)獲取SharedPreferences,只要是Context都能獲取
1.先從mSharedPrefsPaths查詢是否存在相應文
2.如果文件不存在, 則創(chuàng)建新的xml文件; 如果目錄也不存在, 則先創(chuàng)建目錄創(chuàng)建目錄/data/data/package name/shared_prefs/檢查mode
。1.MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE 在android N開始就會拋出異常,防止外部應用讀寫該文件
。2.MODE_MULTI_PROCESS 是多進程方式使用文件,由于有內(nèi)存緩存的緣故這種模式下不怎么能保證進程安全,后續(xù)google不再支持。代替的方式有ContentProvider。創(chuàng)建同名的.bak備份文件用于發(fā)生異常時, 可通過備份文件來恢復數(shù)據(jù).
將xml文件加載到內(nèi)存中,這個操作的線程是新開的,但是會阻塞getXXX()和setxxx()以及edit()方法。
一旦完全加載到內(nèi)存, 后續(xù)的getXXX()則是直接訪問內(nèi)存
3.讀取
- 在xml文件全部內(nèi)加載到內(nèi)存中之前,讀取操作是阻塞的
- 在xml文件全部內(nèi)加載到內(nèi)存中之后,是直接讀取內(nèi)存中的數(shù)據(jù)
4.寫入
- 獲取一個Editor
- putXXX的時候,只是將數(shù)據(jù)寫入到內(nèi)存中的一個mModified Map中
-
執(zhí)行commit 或 apply操作
。 1.commit:- 1.將mModified的數(shù)據(jù)更新到SPI的mMap中
- 2.當沒有key發(fā)生改變, 則直接返回; 否則將mMap全部信息寫入文件, 如果寫入成功則刪除備份文件,如果寫入失敗則刪除mFile
- 3.每次commit是把全部數(shù)據(jù)更新到文件, 所以每個文件的數(shù)據(jù)量必須保證足夠精簡
。 2.apply:
- 1.apply跟commit的最大區(qū)別 在于apply的寫入文件操作是在單線程的線程池來完成.而commit是在當前線程阻塞運行的。
- 2.apply方法開始的時候, 會把一個任務(wù)awaitCommit,放入一個任務(wù)隊列中QueuedWork
- 3.單線程池會不斷從QueuedWork取任務(wù)然后執(zhí)行。
- QueuedWork在這里存在的價值主要是用于在Stop Service, finish BroadcastReceiver過程用于 判定是否處理完所有的異步SP操作.
-
- 總結(jié)
1.apply因為是異步的沒有返回值, commit是同步的有返回值能知道修改是否提交成功
2.多并發(fā)的提交commit時,需等待正在處理的commit數(shù)據(jù)更新到磁盤文件后才會繼續(xù)往下執(zhí)行,從而降低效率; 而apply只是原子更新到內(nèi)存,后調(diào)用apply函數(shù)會直接覆蓋前面內(nèi)存數(shù)據(jù),從一定程度上提高很多效率。
3.edit()每次都是創(chuàng)建新的EditorImpl對象.
- 總結(jié)
5.優(yōu)化
- sp里面存儲特別大的key/value, 有助于減少卡頓/anr
- 不要高頻地使用apply和commit, 盡可能地批量提交。因為每次提交就是xml文件的全部重寫
- commit直接在主線程操作, 要注意
- 不要使用MODE_MULTI_PROCESS,多線程不應該使用SPI
- 高頻寫操作的key與高頻讀操作的key可以適當?shù)夭鸱治募? 由于減少同步鎖競爭
- 不要一上來就執(zhí)行g(shù)etSharedPreferences().edit(),因為getSharedPreferences()是在其他線程進行但是會阻塞.edit()。
- 不要連續(xù)多次edit(), 應該獲取一次獲取edit(),然后多次執(zhí)行putxxx(), 減少內(nèi)存波動。