最近在项目中使用了SharePreferences,因此看了一下SharePreferences相关的源码,在此进行一下记录
SharePreferences在Android提供的几种数据存储方式中属于轻量级的键值存储方式,以XML文件方式保存数据,通常用来存储一些用户行为开关状态等,一般的存储一些常见的数据类型。
SharePreferences保存的xml文件存放于 /data/data/应用程序包/shared_prefs 的目录下
SharePreferences是接口,位于android.content包中
SharePreferencesImpl是对于SharePreferences的实现,位于android.app包下
以下是一段SharePreferences的写入以及读取的例子:
1 |
|
第6行调用了ContextWrapper中的getSharedPreferences方法:
1 |
|
这里调用了mBase的getSharedPreferences,mBase是ContextWrapper持有的Context变量,很明显实际类型即是ContextImpl(Context的实现类):
1 |
|
上面代码首先检查了是否缓存过对应的SharedPreferences,如果没有缓存则创建SharedPreferencesImpl对象(第9行):
1 | SharedPreferencesImpl(File file, int mode) { |
简单说一下这里的几个变量:
- mFile:当前的xml文件,即/data/data/应用程序包/shared_prefs/xxx.xml
- mBackupFile:当前xml备份文件,名为xxx.xml.bak
- mMode:int类型,表示打开的模式
- mLoaded:boolean类型,表示是否已经加载xml文件
- mMap:xml的数据再内存中的储存形式
在SharedPreferencesImpl构造函数中,调用了startLoadFromDisk方法加载xml中的数据:
1 | private void startLoadFromDisk() { |
在startLoadFromDisk方法中,SharedPreferencesImpl开启了新的子线程,并持有对象锁来调用loadFromDiskLocked方法,通过XmlUtils工具类把xml读取为内存中的Map形式,最后通过notifyAll通知所有wait的地方
看完SharedPreferencesImpl的初始化,我们来看一下它的get方法,以例子中的getString为例:
1 | public String getString(String key, String defValue) { |
该方法在持有对象锁的情况下,通过awaitLoadedLocked方法检查是否已经从xml加载到内存map中,然后直接读取map中的数据即可
awaitLoadedLocked方法处理了一些与StrictMode相关的问题,在此不做讨论,在它发现SharedPreferencesImpl还没有加载完毕时,会调用wait方法把当前线程挂起,直到加载完毕通过notifyAll唤醒
看完了SharedPreferencesImpl的get方法,我们再来看一下如何向其中插入新的数据:
1 | public Editor edit() { |
当我们调用edit方法时,会返回一个新的Edit接口的实现类EditorImpl,EditorImpl使用的是默认构造函数,它包含了两个成员变量:
- mModified:Map类型,用于存储修改的键值对
- mClear:boolean类型,用于标识是否清空SharedPreferences
获取EditorImpl实例后,我们会调用其put或者clear方法修改SharedPreferences:
1 | public Editor putString(String key, String value) { |
put方法会把我们的修改存入mModified变量,而clear方法会把mClear变量置为true
当我们修改SharedPreferences完成后,会调用Editor的apply或者commit方法(apply为异步提交,commit为同步提交):
1 | public void apply() { |
两个方法都首先调用了commitToMemory方法,把修改同步到内存中,然后都把写入文件中的任务加入到了队列当中等待执行
写入文件的任务使用的同步工具是CounDownLatch,commit在当前线程调用了await方法等待写入完成并返回Boolean表示写入状态,而apply在把写入任务加入队列后则返回了
在看commitToMemory方法之前,我们先来看看他的返回结果:MemoryCommitResult类
1 | // Return value from EditorImpl#commitToMemory() |
MemoryCommitResult中:writeToDiskResult用于表示写入文件的结果,mapToWriteToDisk表示写入文件的map,即Editor更新后的map
commitToMemory方法如下:
1 | // Returns true if any changes were made |
在第26行我们可以看到,Editor首先通过mClear变量决定是否清空mMap,然后通过mModified变量决定如何修改mMap