天天看点

SharedPreferences源码解析

写这篇博客目的在于巩固自己对SharedPreferences的理解。SharePreferences是Android系统提供的轻量级数据存储方案,主要基于键值对方式保存数据,真实的数据是保存在/data/data/packageName/shared_pref/目录下面的。可以保存多种数据到该文件中,以下是一个简单的Sharepreference文件。

从文件中可以看出就是采用简单xml方式进行保存的。

这里借用Gityuan博客中的类继承图

SharedPreferences源码解析

在Sharepreference中,Sharepreference和Editor只是两个接口,在这两个接口中定义了一个普通的键值对存储的数据一些常用的操作。然后具体你想把这键值对存哪,你可以自己定义相应的文件或数据库,甚至你可以写个保存到网络中去。在Android系统中给出的是采用xml方式存在xml的文件中,具体实现类是SharepreferenceImpl和SharepreferenceImpl.EditorImpl。同时在ContextImpl中有Sharepreference的对应内存中的数据。

Activity.java

Context采用的是装饰模式,其中正在干活的是ContextImpl,mBase即为ContextImpl,具体代码如下:

ContextImpl.java

正在保存的文件获取是getSharedPrefsFile(name),代码如下:

在ContextImpl中存在一个静态的sSharedPrefs,通过它来获取对应应用的prefs,在通过prefs找到对应名称的Sharepreference的引用。在系统中共用一个sSharedPrefs,每个应该在获取sp的时候都会将创建后sp加入到sSharedPrefs中以便后续进行访问。

在获取sp的时候,如果通过sSharedPrefs获取为空就会先创建一个sp,在new SharepreferenceImpl的时候,在构造函数中最后就会异步加载文件到内存,异步开启一个线程后就调用loadFromDiskLocked()函数进行加载:

SharepreferenceImpl.java

一旦加载完成后,就会notifyAll(),我们先看下get的操作

在get数据时,首先判断文件是否加载到内存,然后就直接读取内存中的值,这里可以看出一旦装载了,那么读取的速度就很快。

上面SharepreferenceImpl是实现了get操作,真正的写入是Editor接口来完成的,而EditorImpl是具体的实现类。其代码如下:

首先查看下存入的代码:

存入的时候首先获取同步锁,然后将存入的数据放入EditorImpl中的一个mModified变量中,也就是存入的时候并没有放入Sharepreference中,只有在使用了apply或者commit后才真正存入。

下面来看看commit操作:

步骤1:

步骤2:

步骤3通知写入数据发生变化

步骤4返回写入的结果。

代码如下:

apply会将写入放入到一个线程池中操作,这不会阻塞调用的线程。其他的都和commit类似。

QueuedWork.java

apply 与commit的对比

apply没有返回值, commit有返回值能知道修改是否提交成功

apply是将修改提交到内存,再异步提交到磁盘文件; commit是同步的提交到磁盘文件;

多并发的提交commit时,需等待正在处理的commit数据更新到磁盘文件后才会继续往下执行,从而降低效率; 而apply只是原子更新到内存,后调用apply函数会直接覆盖前面内存数据,从一定程度上提高很多效率。

获取SP与Editor:

getSharedPreferences()是从ContextImpl.sSharedPrefsCache唯一的SPI对象;

edit()每次都是创建新的EditorImpl对象.

优化建议:

强烈建议不要在sp里面存储特别大的key/value, 有助于减少卡顿/anr

请不要高频地使用apply, 尽可能地批量提交;commit直接在主线程操作, 更要注意了

不要使用MODE_MULTI_PROCESS;

高频写操作的key与高频读操作的key可以适当地拆分文件, 由于减少同步锁竞争;

不要一上来就执行getSharedPreferences().edit(), 应该分成两大步骤来做, 中间可以执行其他代码.

不要连续多次edit(), 应该获取一次获取edit(),然后多次执行putxxx(), 减少内存波动; 经常看到大家喜欢封装方法, 结果就导致这种情况的出现.

每次commit时会把全部的数据更新的文件, 所以整个文件是不应该过大的, 影响整体性能;

<a href="http://gityuan.com/2017/06/18/SharedPreferences/">Gityuan博客</a>

<a href="http://blog.csdn.net/yanbober/article/details/47866369">工匠若水</a>