前言
SharedPreferences是Android提供的数据持久化的一种手段,适合单进程、小批量的数据存储与访问。因为SharedPreferences的实现是基于单个xml文件实现的,并且,所有持久化数据都是一次性加载到内存,如果数据过大,是不合适采用SharedPreferences存放的。而适用的场景是单进程的原因同样如此,由于Android原生的文件访问并不支持多进程互斥,所以SharePreferences也不支持,如果多个进程更新同一个xml文件,就可能存在同不互斥问题。
SharedPreferences简单使用
创建
第一个参数是储存的xml文件名称,第二个是打开方式,一般就用Context.MODE_PRIVATE;
SharedPreferences sp=context.getSharedPreferences("名称", Context.MODE_PRIVATE);
复制代码
写入
//可以创建一个新的SharedPreference来对储存的文件进行操作
SharedPreferences sp=context.getSharedPreferences("名称", Context.MODE_PRIVATE);
//像SharedPreference中写入数据需要使用Editor
SharedPreference.Editor editor = sp.edit();
//类似键值对
editor.putString("name", "string");
editor.putInt("age", 0);
editor.putBoolean("read", true);
//editor.apply();
editor.commit();
复制代码
- apply和commit都是提交保存,区别在于apply是异步执行的,不需要等待。不论删除,修改,增加都必须调用apply或者commit提交保存;
- 关于更新:如果已经插入的key已经存在。那么将更新原来的key;
- 应用程序一旦卸载,SharedPreference也会被删除;
读取
SharedPreference sp=context.getSharedPreferences("名称", Context.MODE_PRIVATE);
//第一个参数是键名,第二个是默认值
String name=sp.getString("name", "暂无");
int age=sp.getInt("age", 0);
boolean read=sp.getBoolean("isRead", false);
复制代码
4、检索
SharedPreferences sp=context.getSharedPreferences("名称", Context.MODE_PRIVATE);
//检查当前键是否存在
boolean isContains=sp.contains("key");
//使用getAll可以返回所有可用的键值
//Map<String,?> allMaps=sp.getAll();
复制代码
删除
当我们要清除SharedPreferences中的数据的时候一定要先clear()、再commit(),不能直接删除xml文件;
SharedPreference sp=getSharedPreferences("名称", Context.MODE_PRIVATE);
SharedPrefence.Editor editor=sp.edit();
editor.clear();
editor.commit();
复制代码
- getSharedPreference() 不会生成文件,这个大家都知道;
- 删除掉文件后,再次执行commit(),删除的文件会重生,重生文件的数据和删除之前的数据相同;
- 删除掉文件后,程序在没有完全退出停止运行的情况下,Preferences对象所存储的内容是不变的,虽然文件没有了,但数据依然存在;程序完全退出停止之后,数据才会丢失;
- 清除SharedPreferences数据一定要执行editor.clear(),editor.commit(),不能只是简单的删除文件,这也就是最后的结论,需要注意的地方
源码分析
获取SharedPreferences对象的过程
Context是一个抽象类,它的子类中有ContextImpl和ContextWrapper
Context mBase;
public SharedPreferences getSharedPreferences(String name, int mode) {
//这里其实调用的是ContextImpl的getSharedpreferences方法
return mBase.getSharedPreferences(name, mode);
}
最终还是要看看ContextImpl中的getSharedPreferences的具体实现:
public SharedPreferences getSharedPreferences(String name, int mode) {
//当SDK< 19时,如果文件名为null则会初始化名字为"null"
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
}
}
File file;
synchronized (ContextImpl.class) {
//建立了文件名和File文件的一一对应关系
if (mSharedPrefsPaths == null) {
mSharedPrefsPaths = new ArrayMap<>();
}
file = mSharedPrefsPaths.get(name);
if (file == null) {
//在相应的sharedPreference下新建File,保存文件名为XXX.xml
file = getSharedPreferencesPath(name);
mSharedPrefsPaths.put(name, file);
}
}
return getSharedPreferences(file, mode);
}
新建File的缓存,使用Map建立了文件名和File文件的一一对应,如果没有文件则新建,并放入Map中,如果Map中有需要的文件,则直接使用get方法取出。最终调用了ContextImpl的getSharedPreferences方法。
public SharedPreferences getSharedPreferences(File file, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
//在每个包下面建立了一个File-SharedPreferencesImpl对应关系的ArrayMap
//getSharedPreferencesCacheLocked()根据包名返回相对应包下的ArrayMap
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
//从file-SharedPreferencesImpl键值对中根据当前file找到SharedPreferencesImpl实例
sp = cache.get(file);
//如果没有对应的SharedPreferencesImpl,就新建一个
if (sp == null) {
// 检查mode,如果是MODE_WORLD_WRITEABLE或者MODE_MULTI_PROCESS则直接抛异常
checkMode(mode);
if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
if (isCredentialProtectedStorage()
&& !getSystemService(UserManager.class)
.isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
throw new Il