概述:
SharedPreferences(简称SP)是Android中很常用的数据存储方式,SP采用key-value(键值对)形式,
主要用于轻量级的数据存储, 尤其适合保存应用的配置参数, 但不建议使用SP 来存储大规模的数据, 可能会降低性能.以下分析都是基于android 6.0
SP采用xml文件格式来保存数据, 该文件所在目录位于/data/data//shared_prefs/
1, 使用
1,获取SharedPreferences对象
SharedPreferences userSettings = getSharedPreferences("settingapp",Context.MODE_PRIVATE);
2,获取Editor对象
SharedPreferences.Editor editor = userSettings.edit();
3, 存放数据
editor.putString("name","AA");
editor.putString("URL","BB.com");
4, 完成提交(异步的)
editor.commit();
5,获取数据
String name = userSettings.getString("name"," default ");
String url = userSettings.getString("URL","default");
6,删除数据
editor.remove("KEY");
7, 清空数据
editor.clear();
2, 获取SharedPreferences对象
获取SharedPreferences对象不止一种方法,例如在Activity.java中,可以调用getPreferences方法获取,但是建议
使用getSharedPreferences方法,其实最后getPreferences方法也是调用getSharedPreferences方法完成。其实是有Context的子类ContextImpl完成的。
ContextImpl的getSharedPreferences方法主要逻辑如下,
1, sSharedPrefs变量为空就创建sSharedPrefs
synchronized (ContextImpl.class) {
if (sSharedPrefs == null) {
sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
}
2, packagePrefs为空就创建,
final String packageName = getPackageName();
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
sSharedPrefs.put(packageName, packagePrefs);
}
3, SharedPreferencesImpl为空就创建该对象并返回。
sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.put(name, sp);
return sp;
}
由此可知, sSharedPrefs 变量保存着一个进程中所有的以包名区分的packagePrefs,定义如下,
private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
String 是包名,对应的值也是一个ArrayMap,该ArrayMap中保存着相同包名下创建的String对应的SharedPreferencesImpl对象。
因此,返回的是SharedPreferencesImpl对象, SharedPreferences只是一个接口, SharedPreferencesImpl实现了这个接口。
并且在构造SharedPreferencesImpl对象之前,首先调用getSharedPrefsFile方法构造对应的xml文件。
getSharedPrefsFile方法如下,
public File getSharedPrefsFile(String name) {
return makeFilename(getPreferencesDir(), name + ".xml");
}
getPreferencesDir方法如下,
private File getPreferencesDir() {
synchronized (mSync) {
if (mPreferencesDir == null) {
mPreferencesDir = new File(getDataDirFile(), "shared_prefs");
}
return mPreferencesDir;
}
}
makeFilename方法如下,
if (name.indexOf(File.separatorChar) < 0) {
return new File(base, name);
}
先从mSharedPrefsPaths查询是否存在相应文件;
如果文件不存在, 则创建新的xml文件; 如果目录也不存在, 则先创建目录创建目录/data/data/package name/shared_prefs/
其中mSharedPrefsPaths用于记录所有的SP文件, 是以文件名为key的Map数据结构.
SharedPreferencesImpl构造方法如下,
mFile = file;
//创建为.bak为后缀的备份文件
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
startLoadFromDisk();
同名的.bak备份文件用于发生异常时, 可通过备份文件来恢复数据.
startLoadFromDisk方法如下,
new Thread("SharedPreferencesImpl-load") {
public void run() {
synchronized (SharedPreferencesImpl.this) {
loadFromDiskLocked();
}
}
}.start();
mLoaded用于标记SP文件已加载到内存. 创建线程调用loadFromDiskLocked方法完成从磁盘加载sp文件的工作.
startLoadFromDisk主要逻辑如下,
1,构造Map对象,以及相关输入流
Map map = null;
StructStat stat = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);
2,为相关变量赋值,这些完成之后,才释放线程锁,才可以执行其他方法,
mLoaded = true;//将mLoaded 赋值
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<String, Object>();
}
notifyAll();
3, edit方法
SharedPreferencesImpl的edit方法如下,
synchronized (this) {
awaitLoadedLocked();
}
return new EditorImpl();
awaitLoadedLocked方法如下,
if (!mLoaded) {
BlockGuard.getThreadPolicy().onReadFromDisk();
}
while (!mLoaded) {
try {
wait();//当没有加载完成,则进入等待状态
} catch (InterruptedException unused) {
}
}
EditorImpl 是SharedPreferencesImpl的内部类,实现了Editor接口,定义如下,
public final class EditorImpl implements Editor {
4,数据操作
数据操作主要是调用EditorImpl对象的方法,有2个变量,
private final Map<String, Object> mModified = Maps.newHashMap();
private boolean mClear = false;
mModified 主要是缓存数据, mClear 判断是否清零。
例如,常用的putString方法如下,
public Editor putString(String key, @Nullable String value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
其他保存数据方法如下,
public Editor putStringSet(String key, @Nullable Set<String> values) {
synchronized (this) {
mModified.put(key, (values == null) ? null : new HashSet<String>(values));
return this;
}
}
public Editor putInt(String key, int value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
•••
由此可见,一般只能保存基本的类型的数据和Set 集合。
删除数据的remove方法如下,
public Editor remove(String key) {
synchronized (this) {
mModified.put(key, this);
return this;
}
}
清除数据的clear方法如下,
public Editor clear() {
synchronized (this) {
mClear = true;
return this;
}
}