1.SharedPreferences簡介
??Sharedpreferences是Android平臺上一個輕量級的存儲類道逗,可以用于保存應用程序的各種配置信息振乏,如應用設置里面的各種開關、是否打開音效嚎卫、是否使用震動效果拴疤、小游戲的玩家積分等,其本質是以“鍵-值”對的方式保存數(shù)據(jù)到本地的 xml 文件中依鸥,其文件保存在 /data/data/<package name>/shared_prefs 目錄下。
??核心原理:以“鍵-值”對的方式保存數(shù)據(jù)到本地的 xml 文件中畦徘,具體實現(xiàn)是在 SharedPreferencesImpl 里面使用Map來管理毕籽,xml 文件的具體保存路徑是在 /data/data/<package name>/shared_prefs 目錄下抬闯。SharedPreferences對象本身只能獲取數(shù)據(jù)而不支持存儲和修改井辆,存儲修改是通過SharedPreferences.edit()獲取的內部接口Editor對象實現(xiàn)。
??SharedPreferences本身是一 個接口溶握,程序無法直接創(chuàng)建SharedPreferences實例杯缺,只能通過Context提供的getSharedPreferences(String name, int mode)方法來獲取SharedPreferences實例,該方法中name表示要操作的xml文件名睡榆,第二個參數(shù)具體如下:
Context.MODE_APPEND: 追加方式存儲
Context.MODE_PRIVATE: 指定該SharedPreferences數(shù)據(jù)只能被本應用程序讀萍肆、寫。
Context.MODE_WORLD_READABLE: 指定該SharedPreferences數(shù)據(jù)能被其他應用程序讀胀屿,但不能寫塘揣。
Context.MODE_WORLD_WRITEABLE: 指定該SharedPreferences數(shù)據(jù)能被其他應用程序讀,寫
Context.MODE_MULTI_PROCESS: 適用于多進程訪問(目前已被廢棄宿崭,google官方推薦使用ContentProvider來實現(xiàn)進程間共享訪問)
Editor有如下主要重要方法:
SharedPreferences.Editor clear():清空SharedPreferences里所有數(shù)據(jù)
SharedPreferences.Editor putXxx(String key , xxx value): 向SharedPreferences存入指定key對應的數(shù)據(jù)亲铡,其中xxx 可以是boolean,float,int等各種基本類型據(jù)
SharedPreferences.Editor remove(): 刪除SharedPreferences中指定key對應的數(shù)據(jù)項
boolean commit(): 當Editor編輯完成后,使用該方法提交修改
??首次創(chuàng)建SharedPreferences對象(即SharedPreferences初始化時)葡兑,會根據(jù)文件名將文件下內容一次性加載到mMap容器中奖蔓,每當我們edit都會創(chuàng)建一個新的EditorImpl對象,當修改或者添加數(shù)據(jù)時會將數(shù)據(jù)添加到mModifiled容器中讹堤,然后commit或者apply操作比較mMap與mModifiled數(shù)據(jù)修正mMap中最后一次提交數(shù)據(jù)然后寫入到文件中吆鹤。
??使用SharedPreferences的 get 方法獲取數(shù)據(jù)時是直接從 mMap 中讀取的,直接從 mMap 中讀取數(shù)據(jù)可以提高讀取的效率洲守,但也間接表明 SharedPreferences 不適合存放 大的key和value疑务,因為存放大的key和value在SharedPreferences中,數(shù)據(jù)會一直存儲在內存中得不到釋放占用較大的內存梗醇,容易引發(fā)系統(tǒng) GC暑始,嚴重時導致界面丟幀甚至ANR。
2.SharedPreferences提交數(shù)據(jù)的方法commit()婴削、apply()的區(qū)別及使用場景
commit()廊镜、apply()的區(qū)別
- commit() 方法是Android API 1開始就存在的方法,而 apply() 方法是從Android API 9 開始增加的方法唉俗;
- commit() 和 apply() 雖然都是原子性操作嗤朴,但是原子的操作范圍不同配椭,commit() 是原子提交到數(shù)據(jù)庫,從提交數(shù)據(jù)到存在Disk中都是同步過程雹姊;而 apply() 方法是原子提交到內存股缸,從內存到數(shù)據(jù)庫的更新是異步操作;
- 提交相同的數(shù)據(jù) commit() 方法的效率會比apply() 方法提交的速度慢吱雏,即 apply()方法提交數(shù)據(jù)的效率較高敦姻;
- apply() 沒有返回值,而 commit() 有返回值表明提交修改是否成功歧杏。
使用場景
??從 commit()和 apply() 兩個方法的區(qū)別中可以得出兩個方法的使用場景:在一個進程中镰惦,由于sharedPreference是單實例的,只要保證內存緩存正確就能保證運行時數(shù)據(jù)的正確性犬绒,一般不會出現(xiàn)并發(fā)沖突旺入,所以如果對提交結果不關心的話,建議使用apply()凯力,只有在關心提交結果的情況下使用 commit()茵瘾。
3.Sharedpreferences跨進程訪問問題
??對于多進程的應用,若在某一個進程獲取到的SP值不是最新的咐鹤,很可能是創(chuàng)建SP的時候指定的模式有問題拗秘,應該指定為多進程的模式:Context.MODE_MULTI_PROCESS,設置之后可以實時讀取Sharedpreferences中修改后的值祈惶。
??通過Context.MODE_MULTI_PROCESS屬性使用SharedPreferences雖然可以實現(xiàn)多進程訪問SharedPreferences數(shù)據(jù)的問題雕旨,但是這種方式的多進程共享數(shù)據(jù)可能會出現(xiàn)數(shù)據(jù)不一致的問題。問題原因是因為進程間是不能內存共享的行瑞,每個進程操作的SharedPreferences都是一個單獨的實例奸腺,SharedPreferences數(shù)據(jù)寫入的時機也不確定,而且不能通過加鎖解決多進程的數(shù)據(jù)同步血久,從而導致了多進程間通過SharedPreferences來共享數(shù)據(jù)是不安全的突照。
??結論:Context.MODE_MULTI_PROCESS這個屬性Google已經(jīng)廢棄,不建議使用了氧吐,對于多進程間的數(shù)據(jù)共享建議使用ContentProvider讹蘑。若要用Sharedpreferences實現(xiàn)多進程數(shù)據(jù)共享,只能在確保不會同時操作SharedPreferences數(shù)據(jù)的前提下使用筑舅,但這個條件很難保證座慰,所以建議最好不要使用。
4.訪問其他應用中的Preference
??如果要訪問其他應用中的Preference翠拣,必須滿足的條件是版仔,要訪問的應用的Preference創(chuàng)建時指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE權限。
??舉例,假如有個<package name>為com.alexlee1987.demo下面的應用使用了下面語句創(chuàng)建了Preference蛮粮,getSharedPreferences("demo", Context.MODE_WORLD_READABLE),現(xiàn)在要訪問該Preferences:
首先益缎,需要創(chuàng)建上面的Context,然后通過Context訪問Preferences然想,訪問preference時會在應用所在包下的shared_prefs目錄找到preference:
Context context = createPackageContext("com.alexlee1987.demo", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = context.getSharedPreferences("demo", Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
??如果不通過創(chuàng)建Context訪問其他應用的preference莺奔,可以以讀取xml文件方式直接訪問其他應用preference對應的xml文件,如:
File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);//<package name>應替換成應用的包名变泄。
5.使用建議
??Sharedpreferences是Android平臺上一個輕量級的存儲類令哟,可以方便快捷的在本地保存應用的一些信息,Sharedpreferences好用妨蛹,但也不能濫用屏富,使用過程中建議遵循以下規(guī)則:
- 不要存放大的key和value在SharedPreferences中,數(shù)據(jù)一直存儲在內存中得不到釋放滑燃,內存使用過高會頻發(fā)引發(fā)GC役听,導致界面丟幀甚至ANR颓鲜;
- 不相關的配置選項最好不要放在一起表窘,單個文件越大讀取速度則越慢;
- 讀取頻繁的key和不頻繁的key盡量不要放在一起甜滨;
-
commit發(fā)生在UI線程中乐严,apply發(fā)生在工作線程中,對于數(shù)據(jù)的提交最好是批量操作統(tǒng)一提交衣摩。雖然apply發(fā)生在工作線程(不會因為IO阻塞UI線程)但是如果添加任務較多昂验,Activity頁面退出時有可能會阻塞,嚴重時甚至會出現(xiàn)ANR艾扮,具體可以參照ActivityThread源碼中handleStopActivity方法實現(xiàn)既琴;
handleStopActivity源碼 - 盡量不要存放json和html,這種可以直接文件緩存泡嘴;
- 最好提前初始化SharedPreferences甫恩,避免SharedPreferences第一次創(chuàng)建時讀取文件線程未結束而出現(xiàn)等待情況,可以考慮在Application初始化的時候初始化SharedPreferences酌予。