SharedPreferences(后續(xù)簡稱SP)為我們提供了輕量級存儲能力,方便了少量數(shù)據(jù)的持久化。
但是由于項目越來越龐大,SP操作使用不當(dāng)會導(dǎo)致app卡頓,乃至ANR問題恬吕。
下面介紹一下操作SP的優(yōu)化點。
SP性能優(yōu)化點
SP性能變差的原因有很多须床。
1.原生API的限制主要有以下兩方面:
?????(1)IO瓶頸
?????(2)鎖性能差
2.對SP的不當(dāng)封裝也會間接造成數(shù)據(jù)讀寫性能差铐料。
下面會對以上三方面進行分析。
IO瓶頸
IO瓶頸造成SP性能差是最大的原因豺旬,解決了IO瓶頸钠惩,80%的性能問題就解決了。
SP的IO瓶頸包括讀取數(shù)據(jù)到內(nèi)存與數(shù)據(jù)寫入磁盤兩部分族阅。
1.讀取數(shù)據(jù)到內(nèi)存有兩個場景會觸發(fā):
?????(1)SP文件沒有被加載到內(nèi)存時篓跛,調(diào)用getSharedPreferences方法會初始化文件并讀入內(nèi)存。
?????(2)版本低于android_H或使用了MULTI_PROCESS標志時坦刀,每次調(diào)用getSharedPreferences方法時都會讀入愧沟。
? ? ?我們可以優(yōu)化的便是(2)了。每次加載數(shù)據(jù)到內(nèi)存太過影響效率鲤遥。
? ?? H以下版本留存率已經(jīng)很低了沐寺,基本可以忽略。
?????對于MULTI_PROCESS渴频,可以采用ContentProvider等其他方式芽丹,效率更好,而且可避免SP數(shù)據(jù)丟失的情況卜朗。
2.數(shù)據(jù)寫入磁盤也有兩個場景會觸發(fā):
?????(1)Editor的commit方法,每次執(zhí)行時同步寫入磁盤咕村。
?????(2)Editor的apply方法场钉,每次執(zhí)行時在單線程池中加入寫入磁盤Task,異步寫入懈涛。
?????commit和apply的方法區(qū)別在于同步寫入和異步寫入逛万,以及是否需要返回值。
? ?? 在不需要返回值的情況下批钠,使用apply方法可以極大的提高性能宇植。
?????同時,多個寫入操作可以合并為一個commit/apply埋心,將多個寫入操作合并后也能提高IO性能指郁。
鎖性能差
SP的get操作,會鎖定SharedPreferences對象拷呆,互斥其他操作闲坎。
SP的put操作疫粥,getEditor及commitToMemory會鎖定SharedPreferences對象,put操作會鎖定Editor對象腰懂,寫入磁盤更會鎖定一個寫入鎖梗逮。
由于鎖的緣故,SP操作并發(fā)時绣溜,耗時會徒增慷彤。減少鎖耗時,是另一個優(yōu)化點怖喻。
由于讀寫操作的鎖均是針對SP實例對象的底哗,將數(shù)據(jù)拆分到不同的sp文件中,便是減少鎖耗時的直接方案罢防。
降低單文件訪問頻率艘虎,多文件均攤訪問,以減少鎖耗時咒吐。
用開發(fā)機進行了簡單的性能測試(寫入均使用apply野建,若使用commit則多線程耗時更高):
讀寫同一文件,10個線程每個讀寫10次數(shù)據(jù):
耗時80-130ms
讀寫10個文件恬叹,每個文件由1個線程讀寫10次數(shù)據(jù):
耗時30-70ms
對SP操作的不當(dāng)封裝
由于我們項目采用了插件化候生,所以對SP的操作涉及到了跨進程訪問。
我們采用ContentProvider方案支持跨進程訪問绽昼,并對所有SP操作均套上了ContentProvider進行訪問唯鸭。
隨著項目越來越龐大,通過ContentProvider訪問造成的耗時性能也成了問題硅确。
對ContentProvider操作SP測試目溉,耗時是直接操作SP的4倍左右。
所以菱农,最近項目中進行了SP的處理缭付,對于不需要跨進程的SP操作去掉了ContentProvider,盡可能減少無謂耗時循未。
SP優(yōu)化的建議
1.盡量不要直接調(diào)用SharedPreferences進行讀寫操作陷猫。
若直接調(diào)用getSharedPreferences(fileName,mode).edit().putString(key,value),則對數(shù)據(jù)的操作直接耦合了fileName和key的妖,后續(xù)想調(diào)整file和key會比較困難绣檬。
可以考慮封裝一下,譬如:
public void saveUserId(){
? ? ?getSharedPreferences(fileName,mode).edit().putString(“user_id”,value);
}
這樣做可以直接對數(shù)據(jù)訪問嫂粟,而與fileName與key解耦娇未,后續(xù)拆分與調(diào)整時會很方便。
2.將SP作為耗時操作對待赋元,盡量減少無謂的調(diào)用忘蟹。
譬如以下代碼飒房,SP讀一次即可:
if(sp.getUserId()>0){
? ? ?int id=sp.getUserId();
? ? ?...
}