一,概述
SharePreference作为轻量级应用偏好持久化存储API,可以简单存储一些key-value值,其实现是基于file系统,在应用data/xxx/shared_prefs路径下,保存了xml文件,而实现存储。本文,基于SharePreference的基本使用,到其内部实现,做一个简单介绍。
二,简例
key-value api如下
//1,从context中获得SharePreference实现类实例
SharedPreferences sharedPreferences = context.getSharedPreferences("share_name", Context.MODE_PRIVATE);
//2,read 方法
boolean value1 = sharedPreferences.getBoolean("key1", false);
float value2 = sharedPreferences.getFloat("key2", 1f);
int value3 = sharedPreferences.getInt("key3", 1);
long value4 = sharedPreferences.getLong("key4", 1L);
String value5 = sharedPreferences.getString("key5", "default");
Set<String> value6 = sharedPreferences.getStringSet("key6", Collections.emptySet());
//3,write方法
SharedPreferences.Editor edit = sharedPreferences.edit();
edit.putBoolean("key1",false);
edit.putFloat("key1",1f);
edit.putInt("key1",1);
edit.putLong("key1",1L);
edit.putString("key1","false");
edit.putStringSet("key1",Collections.emptySet());
//提交,commit同步sync,apply是async,不阻塞当前线程
edit.commit();
edit.apply();
创建flag以下可选
MODE_PREVATE:私有,其它应用不可读取
MODE_WORLD_READABLE,其它应用只可读取
MODE_WORLD_WRITEABLE,其它应用可写可读
MODE_MULTI_PROCESS,已弃用,代表多进程相关
三,实现
1,初始化
context.getSharePreference(String name,int flags)方法返回一个SharePreferenceImpl,其实现在ContextImpl中,
1,创建一个ArrayMap,存储name与file路径的映射
2,返回name对应的本地路径
getPreferencesDir()方法返回/data/xxx/shared_prefs路径,随后创建一个name.xml的file
3,返回一个SharePreferenceImpl,跟进
1,2,Sp与File的缓存
3,多进程FLAG相关,
跟进SharePreferenceImpl实现类,
构造方法中创建了一个file的备份,并通过startLoadFromDisk异步加载磁盘内容到内存中,
使用的线程池参数如下,最大线程数1,核心线程数0,存活10s,
跟进loadFromDisk
1,备份文件存在,使用备份文件加载,并且重命名为主文件。此举在于SharePreference未保存发生错误时,可以恢复数据而不至于丢失。
2,文件可读,
3,读取对应xml文件,并且通过XmlUtils.readMapXml解析,并保存至mMap中,
此处,可以看下应用中存储的file,内容如下,
2,read
随便看一个getString方法,
通过mLock加锁,再从mMap中取值,跟进awaitLoadedLocked方法
mLoaded在初始化完毕后,才为true,因此此处相当于等待loadFromDisk任务完成,通过mLock.notifyAll方法通知到此处,
3,write
以putWrite为例
加载完毕后,直接返回一个EditorImpl,跟进
以上,将write事件全写进mModified map中,通过commit或apply再写入磁盘,以减少io次数,
跟进commit
1,通过commitToMemory,返回一个MemoryCommitResult,可以理解一次写事务,内部是通过mModified更新mMap,并保存至mapToWriteToDisk中,如下,
2,将写事务加入队列中执行,
以上,创建一个writeRunnable,将mcr传入,并且加入队列,
3,等待写入CountLaunch
以上,io如果ok,则down一次。
4,通知Observers
注意,其IO操作进入QueueWork队列,在如下线程操作,
看下apply
不同点在于是否在当前线程执行Condition#await
以上,创建一个postWriteRunnable,此runnable即await一次,执行时机移至writeToFile后,
将await时机延后&执行线程更换至子线程,避免Queue排队以及io时阻塞当前线程,执行io线程同commit,仍在"queued-work-looper"。而writeToFile时已经将Count计数归0,postWriteRunnable不会阻塞。