關于SharedPreference踩的那些坑
看文章之前,首先思考一個問題:SharedPreference支持多進程嗎?如果你知道的話,下面的內容可能對你沒有太大的幫助,可以快讀閱讀,復習一下或者指教我寫的不對的地方,如果你猶豫不決,或者不知道的話,下面的內容可能會對你有些幫助。
SharedPreferences多進程
為什么會突然寫這篇文章呢,主要是我昨天在用 SharedPreferences 的時候,涉及到多進程訪問,寫的時候沒注意,然后導致數據不對,既然踩了坑,肯定要自己反思一下總結,所以也就有了這篇文章,簡單總結一下,
首先直截了當得先回答文章開頭那個問題,答案是:能但是不行。
為什么這么說呢,因為 SharedPreferences 確實可以通過設置 MODE_MULTI_PROCESS 實現多進程訪問,而且是SDK2.3 之前是默認的,連這個標志都不用設置,SDK2.3之后就需要手動設置,既然這么說了,那就肯定是能了,但是為什么說不行呢?因為這個標志在 SDK6.0 的時候已經被Deprecated,Android為什么要把這么一個簡單易用的進程間通訊方式廢棄掉呢,看下源碼就可以知道,原因就是它并不能保證數據的安全性和準確性,并沒有實現并發修改的機制,所以官方也就不推薦了,而是推薦使用 ContentProvider。總結一句話:愛過。
基礎知識大普及
既然看到了源碼,索性就看完整一點,這樣以后用的時候也更順手一點,于是簡單的把 SharedPreferences 相關的內容簡單都看了下,下面就是自己簡單的一些筆記:
我平時在使用 SharedPreferences 一般會直接用:
SharedPreferences sp2 = PreferenceManager.getDefaultSharedPreferences(this);
來獲得一個 SharedPreferences 的引用來操作,正好借著這個機會好好看了下官方的介紹,終于算是對 SharedPreferences 有一個更完善的認識了,
SharedPreferences sp1 = getSharedPreferences(getPackageName() + "test", MODE_PRIVATE); SharedPreferences sp2 = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sp3 = getPreferences(MODE_PRIVATE);
上面的三行代碼都是為了獲得一個 SharedPreferences 引用,其中第三個方法是 Activity 專有的,其實我們看源碼的話,就會發現其實后兩個方法其實都是第一個方法的封裝
public static SharedPreferences getDefaultSharedPreferences(Context context) { return context.getSharedPreferences(getDefaultSharedPreferencesName(context), getDefaultSharedPreferencesMode()); } private static String getDefaultSharedPreferencesName(Context context) { return context.getPackageName() + "_preferences"; } private static int getDefaultSharedPreferencesMode() { return Context.MODE_PRIVATE; }
正如你所見, getDefaultSharedPreferences() 就是返回了一個名字為 getPackageName() + "_preferences" ,mode 為 MODE_PRIVATE 的實例。
public SharedPreferences getPreferences(int mode) { return getSharedPreferences(getLocalClassName(), mode); }
而 getPreferences 就是返回了一個名字是以當前類名命名的,mode自定義的實例。
簡單說完獲得一個 SharedPreferences 引用的三個方法之后,簡單看下這兩個參數: name, mode, 分別是干嘛用的,首先說下 name 這個參數,正如源碼中所說的,你只要知道了 name ,你就能訪問到這個 SharedPreferences 中的所有數據,就是說 name 是 SharedPreferences 在系統的唯一標示,從這里,我想到的是,以后做多用戶登錄,保存多用戶配置信息的時候,就可以用包名+用戶名,設置不同的 name 來實現,這樣用戶注銷或者刪除之后,直接把對應 name 的 SharedPreferences 清空掉就OK了,不同 name 對應不同的 SharedPreferences 文件,在這里強插一句, SharedPreferences 在手機中的保存方式其實是以xml文件的形式存儲的,會在:data\data\程序包名\shared_prefs目錄下,生成一個以 name 命名的xml文件。
說完了 name ,說下第二個參數: mode , 源碼中提示, SharedPreferenced 支持三個不同的mode,
- MODE_PRIVATE
- MODE_WORLD_READABLE
- MODE_WORLD_WRITEABLE
之前無感知的,一般用的都是 MODE_PRIVATE , 也是現在唯一的mode,因為另外兩個mode已經被廢棄掉了, mode 指定的是其他應用是否可以訪問或者修改指定的 SharedPreferences , 從字面上就能看的出來, MODE_PRIVATE 表示其他應用不可讀,不可寫, MODE_WORLD_READABLE 表示其他應用可讀但不可寫, MODE_WORLD_WRITEABLE 表示其他應用可讀可寫,但是現在后兩個mode已經被廢棄掉了,官方給的提示是:全局可見的文件是很為危險的方式,容易導致安全漏洞,推薦更普遍的通信方式,比如: ContentProvider , BroadcastReceiver 或者 Service , 而且因為這個權限設置是在生成 SharedPreferences 文件的時候就設置的,但是不能保證這個權限設置在備份或者恢復的時候保留。總而言之,一句話,還是用 MODE_PRIVATE 吧,應用間通信不要考慮 SharedPreferences 這種方式,如果看到老的一些文章或者書,有推薦這種方式的,自己留意就好。
獲得一個 SharedPreferences 之后,我們就可以對它進行讀取或者寫入了,這些大家有用過的都會比較熟悉,所以不在這里多寫什么,無非就是 SharedPreferences 支持幾種基本的數據類型: int , float , long , String , boolean ,還有一種在我看來比較特殊的: Set<String> ,寫入的方式:同步 commit() 和異步 apply() 。這些都是日常會用到的,下面主要說一些可能大家沒有用過的(其實是我自己沒用過,所以覺得比較神奇):
-
contains(String key)
這個方法我是第一次見到,之前因為沒有這方面的需求,所以不太了解,這次看到了,就記一下吧。這個方法一眼就能看出來干嘛用的,所以就不多解釋了。
-
registerOnSharedPreferenceChangeListener(listener)
這個方法才是我要說的重點,因為之前有些需求就是更改了 SharedPreferences 之后,要通知相應的組件做出改變,我以前的處理方式是通過事件訂閱實現的,發一個event出去,然后目標收到event再做出反應,當時覺得特別蛋疼,兩邊都要做些操作,顯的特別啰嗦,當時就在想可不可以在 SharedPreferences 上設置一個觀察者,一旦有什么風吹草動,就自動通知目標,不曾想,人家早已經實現了,只是我愚昧無知,今天去看了下源碼發現了這個方法,相見恨晚。
sp1.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // do any thing you want } });
就是這樣,在你想監聽的 SharedPreferences 的引用上設置你的 Listener ,這樣,當它有更改的時候,會自動通知所有的 Listener ,然后在Listener中做你想做的事吧。
題外話
其實就是扯閑篇,和 SharedPreferences 相關的還有一個類 Preference ,雖然他們沒有什么直接關系,前者是用來存儲鍵值對,而后者是用來幫助開發者構建界面的,主要是設置界面,唯一的相關性可能就是 Preference 是用 SharedPreferences 來保存設置信息的,但是如果有時間的話,你還是可以看下 Preference ,你就知道為什么有些應用的設置界面怎么做到的那么遵守 Material Design,其實是現成的…,看完你就懂了。
來自:http://shaohui.xyz/2016/10/20/關于SharedPreference踩的那些坑/