SP的特点以及基本使用方式
SharedPreferences因非常适合存储较小键值集合数据且使用非常简单的特点,而受到广大程序员们热爱。
SP使用非常简单:
//读操作
Context context = getActivity();
SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
String result = sp.getString("key", "defaultValue");
//写操作
Context context = getActivity();
SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
sp.edit().putString("key", "value").commit();
SP源码分析
SP是如何读取数据的
其实Context实现就是ContextImpl中,要想搞清楚SP是如何读取数据的,第一步当然是要了解ContextImpl.getSharedPreferences方法是如何实现的
/**
* Map from package name, to preference name, to cached preferences.
*/
//缓存所有应用的SP容器,该容器key对应应用名称,value则为每个应用存储所有sp的容器(ArrayMap)
private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
if (sSharedPrefs == null) {
//如果静态对象不存在,直接创建一个Map,以便后期用于保存sp
sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
}
//获取当前应用包名
final String packageName = getPackageName();
//从保存sp的容器中通过包名查找当前应用所有sp;每个app的所有sp都是保存在ArrayMap中,
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
if (packagePrefs == null) {
//如果从sp容器没找到保存当前应用sp的ArrayMap直接创建一个
packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
//将创建的对象保存到sp容器中
sSharedPrefs.put(packageName, packagePrefs);
}
// At least one application in the world actually passes in a null
// name. This happened to work because when we generated the file name
// we would stringify it to "null.xml". Nice.
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
//如果targetSdk版本好小于19,且传入的文件名为null的话,默认将文件名命名为"null"
if (name == null) {
name = "null";
}
}
//从当前应用的sp容器中通过文件名去查找sp
sp = packagePrefs.get(name);
if (sp == null) {
//如果没找到,直接创建一个文件名以name命名的xml文件
File prefsFile = getSharedPrefsFile(name);
//此处极为关键,该构造器是读取文件操作
sp = new SharedPreferencesImpl(prefsFile, mode);
//将创建sp对象保存到当前应用sp容器中
packagePrefs.put(name, sp);
return sp;
}
}
if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
// If somebody else (some other process) changed the prefs
// file behind our back, we reload it. This has been the
// historical (if undocumented) behavior.
//如果读取模式是跨线程或targetSdk版本小于11,再次重新load下文件而已
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
从上面源码可以看出,getSharedPreferences方法是先根据当前应用名称来获取一个ArrayMap(存储sp的容器)如果没有直接创建并保存到内存中,然后再根据文件名来获取SharedPreferencesImpl的对象(没找到则直接创建SharedPreferencesImpl),这短短的三十几行代码还是比较简单的。
因为是静态变量存储键值数据的所以我们用SP存储的数据在内存中是一直存在;所以我们尽量用一个文件来存在数据,以达到减少内存对象
大家可以看到getSharePreferences返回的对象类型其实是SharedPreferencesImpl类型,只不过该类实现了SharedPreferences接口而已,接下来我们先看看SharedPreferencesImpl构造器做了啥东东。
SharedPreferencesImpl.java
final class SharedPreferencesImpl implements SharedPreferences {
private final File mFile;
private final File mBackupFile;
private final int mMode;
private Map<String, Object> mMap; // guarded by 'this'
private int mDiskWritesInFlight = 0; // guarded by 'this'
//文件是否加载成功
private boolean mLoaded = false; // guarded by 'this'
//文件的时间以及大小
private long mStatTimestamp; // guarded by 'this'
private long mStatSize; // guarded by 'this'
private final Object mWritingToDiskLock = new Object();
private static final Object mContent = new Object();
private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
SharedPreferencesImpl(File file, int mode) {
//给类成员变量赋值
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
//开启一个线程读取文件
startLoadFromDisk();
}
private static File makeBackupFile(File prefsFile) {
return