Android学习笔记:SharedPreferences

SharedPreferences是Android平台提供的一个轻量级的存储类,能够轻松的存储数据和读取数据,特别适合用于保存软件配置参数。SharedPreferences是使用键值对的方式来存储数据的(其背后是用XML文件存放数据)。当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。SharedPreferences支持多种不同的数据类型存储,比如String、int、boolean值等。

应用范围许多程序的偏好设置(默认设置)功能其实都使用到了SharedPreferences技术


一、将数据存储到SharedPreferences中

要想使用SharedPreferences来存储数据,首先需要获取到SharedPreferences对象。Android中主要提供了3种方法用于得到SharedPreferences对象。

1、Context类中的getSharedPreferences()方法

此方法接受两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个。第二个参数用于指定操作模式,目前只有MODE_PRIVATE这一种模式可选,它是默认的操作模式,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。

2、Activity类中的getPreferences()方法

此方法和Context中的getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名。

3、PreferenceManager类中的getDefaultSharedPreferences()方法

这是一个静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件。

使用以上三种方法得到SharedPreferences对象之后,就可以开始向SharedPreferences文件中存储数据了,主要可以分为3步实现。
(1)调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象。
(2)向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推。
(3)调用apply()方法将添加的数据提交,从而完成数据存储操作。

//1、通过sharedPreferences()方法指定sharedPreferenced的文件名为data,并得到了SharedPreferences.Editor对象
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
//2、向这个对象中添加3条不同类型的数据
editor.putString("name", "haobi");
editor.putInt("age", 18);
editor.putBoolean("married", false);
//3、调用apply()方法进行提交数据,从而完成了数据存储的操作
editor.apply();

二、从SharedPreferences中读取数据

SharedPreferences对象中提供了一系列的get方法,用于对存储的数据进行读取,每种get方法都对应了SharedPreferences.Editor中的一种put方法,比如读取一个布尔类型数据就使用getBoolean()方法,读取一个字符串就使用getString()方法。这些get方法都接收两个参数第一个参数是键,传入存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,即表示当传入的键找不到对应的值时会以什么样的默认值进行返回。

SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
String name = pref.getString("name", "");
int age = pref.getInt("age", 0);
boolean marrid = pref.getBoolean("married", false);

三、关于SharedPreferences存储的几个问题

1、每次调用getSharedPreferences时都会创建一个SharedPreferences对象吗?这个对象具体是哪个类对象?
答:不是,只要name相同,就会返回同一个SharedPreferencesImpl对象,packagePrefs存放文件name与SharedPreferencesImpl键值对,sSharedPrefs存放包名与ArrayMap键值对。注意sSharedPrefs是static变量,也就是一个类只有一个实例,因此你每次getSharedPreferences其实拿到的都是同一个SharedPreferences对象。

2、在UI线程中调用getXXX有可能导致ANR吗?
答:有可能的,getXXX之前,会给当前线程加锁,如果sp文件特别大,查询非常耗时的时候,有可能ANR。

3、为什么SharedPreferences只适合用来存放少量数据,为什么不能把SharedPreferences对应的xml文件当成普通文件一样存放大量的数据?
答:其实这个和第二个问题没什么区别,因为SharedPreferences是整个文件都加载到内存中,文件太大了会对内存造成压力。

4、commit和apply有什么区别?
答:①commit和apply虽然都是原子性操作,但是原子的操作不同,commit是原子提交到数据库,所以从提交数据到存在Disk中都是同步过程,中间不可打断。
②而apply方法的原子操作是原子提交到内存中,而非数据库,所以在提交到内存中时不可打断,之后再异步提交数据到数据库中,因此也不会有相应的返回值。
③所有commit提交是同步过程,效率会比apply异步提交的速度慢,但是apply没有返回值,永远无法知道存储是否失败。
④commit发生在UI线程,而apply发生在工作线程。(关于UI线程与Worker线程,请点击
⑤在不关心提交结果是否成功的情况下,优先考虑apply方法。
(commit()方法会同步地将偏好值(Preference)直接写入持久化存储设备,而apply()方法会立即把修改内容提交到SharedPreferences内容缓存中,然后开始异步的将修改提交到存储设备上,在这个过程中,开发者不会察觉到任何错误问题)

5、apply一定安全吗?
答:不一定。虽然apply使写入文件操作发生在工作线程中,这样防止IO操作阻塞UI线程。假如我们apply非常多的任务,而线程池队列是串行执行,当我们关闭Activity时:会检查线程池队列中任务是否已经全部执行完成,否则一直等到全部执行完成。如果此时等待超过5s,会造成程序崩溃。

6、SharedPreferences每次写入时是增量写入吗?
答:不是,SharedPreferences每次写入都是整个文件重新写入,不是增量写入。SharedPreferences在写入时会把之前的xml文件改名成一个备份文件,然后再将要写入的数据写入到一个新的文件中,如果这个过程执行成功的话,就会把备份文件删除。由此可见每次即使只是添加一个键值对,也会重新写入整个文件的数据,这也说明SharedPreferences只适合保存少量数据,文件太大有性能问题。


四、优化建议

1、SharedPreferences是线程安全的不是进程安全的,内部由大量synchronized关键字保障。

2、不要存放大的key和value在SharedPreferences中,否则会一直存储在内存中得不到释放,内存使用过高会频发引发GC,导致界面丢帧甚至ANR。

3、第一次getSharedPreferences会读取磁盘文件,后续的getSharedPreferences会从内存缓存中获取。如果第一次getSharedPreferences时还没从磁盘加载完毕就调用get/put操作,则所有的get/put操作均会卡住等待,直到数据从磁盘加载完毕后返回,所以主线程第一次调用会有ANR风险。

4、每次apply/commit都会把全部的数据一次性写入磁盘,所以单个的配置文件不应该过大,单个文件越大读取速度越慢,影响整体性能。

5、不要每次都edit,因为每次都会创建一个新的EditorImpl对象,最好是批量处理统一提交。否则edit().commit每次创建一个EditorImpl对象并且进行一次IO操作,严重影响性能。

6、commit发生在UI线程中,apply发生在工作线程中,对于数据的提交最好是批量操作统一提交。虽然apply发生在工作线程(不会因为IO阻塞UI线程)但是如果添加任务较多也有可能带来其他严重后果(参照ActivityThread源码中handleStopActivity方法实现)。

7、提前初始化SharedPreferences,避免SharedPreferences第一次创建时读取文件线程未结束而出现等待情况。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值