android sharedpreferences 线程,Android SharedPreferences总结及优化

一、SharedPreferences简介

Android 中的 SharedPreferences(后续简称SP)是轻量级的数据存储方式,能够保存简单的数据类型,比如 String、int、boolean 值等。应用场合主要是数据比较少的配置信息。其内部是以 XML 结构保存在 /data/data/包名/shared_prefs 文件夹下,数据以键值对的形式保存。

使用Preference来存取数据,用到了SP接口和SP的一个内部接口SharedPreferences.Editor,这两个接口在android.content包中。

调用Context.getSharedPreferences(String name,int mode)方法得到SP接口。该方法的第一个参数是文件名称,第二个参数是操作模式。操作模式有三种:MODE_PRIVATE(私有)、MODE_WORLD_READABLE(可读)、MODE_WORLD_WRITEABLE(可写)。

SP提供了获得数据的方法,如getString(String key,String defValue)、getInt(String key,int defValue)等。调用SP的edit()方法返回SharedPreferences.Editor内部接口,该接口中提供了保存数据的方法,如putString(String key,String value),putInt(String key,int value)等,调用该接口的commit()方法可以将数据进行保存。

二、SP性能优化点

SP性能变差的原因有很多。

1、原生API的限制

(1)IO瓶颈

IO瓶颈造成SP性能差是最大的原因,解决了IO瓶颈,80%的性能问题就解决了。

SP的IO瓶颈包括读取数据到内存与数据写入磁盘两部分。

读取数据到内存有两个场景会触发:

SP文件没有被加载到内存时,调用getSharedPreferences方法会初始化文件并读入内存。版本低于android_H或使用了MULTI_PROCESS标志时,每次调用getSharedPreferences方法时都会读入。

我们可以优化的便是(2)了。每次加载数据到内存太过影响效率。

H以下版本留存率已经很低了,基本可以忽略。

对于MULTI_PROCESS,可以采用ContentProvider等其他方式,效率更好,而且可避免SP数据丢失的情况。

数据写入磁盘也有两个场景会触发:

Editor的commit方法,每次执行时同步写入磁盘。Editor的apply方法,每次执行时在单线程池中加入写入磁盘Task,异步写入。

commit和apply的方法区别在于同步写入和异步写入,以及是否需要返回值。

在不需要返回值的情况下,使用apply方法可以极大的提高性能。

同时,多个写入操作可以合并为一个commit/apply,将多个写入操作合并后也能提高IO性能。

(2)锁性能差

SP的get操作,会锁定SharedPreferences对象,互斥其他操作。

SP的put操作,getEditor及commitToMemory会锁定SharedPreferences对象,put操作会锁定Editor对象,写入磁盘更会锁定一个写入锁。

由于锁的缘故,SP操作并发时,耗时会徒增。减少锁耗时,是另一个优化点。

由于读写操作的锁均是针对SP实例对象的,将数据拆分到不同的sp文件中,便是减少锁耗时的直接方案。

降低单文件访问频率,多文件均摊访问,以减少锁耗时。

用开发机进行了简单的性能测试(写入均使用apply,若使用commit则多线程耗时更高):

读写同一文件,10个线程每个读写10次数据:

耗时80-130ms

读写10个文件,每个文件由1个线程读写10次数据:

耗时30-70ms

2、对SP的不当封装也会间接造成数据读写性能差。

由于我们项目采用了插件化,所以对SP的操作涉及到了跨进程访问。

我们采用ContentProvider方案支持跨进程访问,并对所有SP操作均套上了ContentProvider进行访问。

随着项目越来越庞大,通过ContentProvider访问造成的耗时性能也成了问题。

对ContentProvider操作SP测试,耗时是直接操作SP的4倍左右。

所以,最近项目中进行了SP的处理,对于不需要跨进程的SP操作去掉了ContentProvider,尽可能减少无谓耗时。

三、SP优化的建议

1、尽量不要直接调用SharedPreferences进行读写操作。

若直接调用getSharedPreferences(fileName,mode).edit().putString(key,value),则对数据的操作直接耦合了fileName和key,后续想调整file和key会比较困难。

可以考虑封装一下,譬如:

public void saveUserId(){

getSharedPreferences(fileName,mode).edit().putString(“user_id”,value);

}

这样做可以直接对数据访问,而与fileName与key解耦,后续拆分与调整时会很方便。

2、将SP作为耗时操作对待,尽量减少无谓的调用。

譬如以下代码,SP读一次即可:

if(sp.getUserId()>0){

int id=sp.getUserId();

...

}五、其他程序访问本程序的配置数据方式

通过SharedPreferences创建的配置文件,不需要指定路径和文件后缀名,读取的时候也是。通常情况下,配置只是提供给本应用程序使用的。在这里我们介绍一个小知识点,即其他程序想使用本应用程序的配置,那应该如何使用SharedPreferences呢?如下:

Context otherAppContext = createPackageContext("com.gary.appdisplaycontrol", Context.CONTEXT_IGNORE_SECURITY);

SharedPreferences sharedPreferences = otherAppContext.getSharedPreferences("preferences",Context.MODE_WORLD_READABLE|Context.MODE_MULTI_PROCESS);

备注:必须要添加Context.MODE_MULTI_PROCESS属性,否则会遇到其他程序读取数据未更新问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值