boolean contains(String key);
//针对preferences创建一个新的Editor对象,通过它你可以修改preferences里的数据,并且原子化的将这些数据提交回SharedPreferences对象
Editor edit();
//注册一个回调函数,当一个preference发生变化时调用
void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
//注销一个之前(注册)的回调函数
void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
}
很明显的可以看见,SharePreference源码其实是很简单的。既然这里说了SharePreference类只是一个接口,那么他一定有自己的实现类的,怎么办呢?我们继续往下看。
3-2 SharePreferences实现类SharePreferencesImpl分析
我们从上面SharePreference的使用入口可以分析,具体可以知道SharePreference的实例获取可以通过两种方式获取,一种是Activity的getPreferences方法,一种是Context的getSharedPreferences方法。所以我们如下先来看下这两个方法的源码。
先来看下Activity的getPreferences方法源码,如下:
public SharedPreferences getPreferences(int mode) {
return getSharedPreferences(getLocalClassName(), mode);
}
哎?可以发现,其实Activity的SharePreference实例获取方法只是对Context的getSharedPreferences再一次封装而已,使用getPreferences方法获取实例默认生成的xml文件名字是当前activity类名而已。既然这样那我们还是转战Context(其实现在ContextImpl中,至于不清楚Context与ContextImpl及Activity关系的请先看这篇博文,点我迅速脑补)的getSharedPreferences方法,具体如下:
//ContextImpl类中的静态Map声明,全局的一个sSharedPrefs
private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
//获取SharedPreferences实例对象
public SharedPreferences getSharedPreferences(String name, int mode) {
//SharedPreferences的实现类对象引用声明
SharedPreferencesImpl sp;
//通过ContextImpl保证同步操作
synchronized (ContextImpl.class) {
if (sSharedPrefs == null) {
//实例化对象为一个复合Map,key-package,value-map
sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
}
//获取当前应用包名
final String packageName = getPackageName();
//通过包名找到与之关联的prefs集合packagePrefs
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
//懒汉模式实例化
if (packagePrefs == null) {
//如果没找到就new一个包的prefs,其实就是一个文件名对应一个SharedPreferencesImpl,可以有多个对应,所以用map
packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
//以包名为key,实例化的所有文件map作为value添加到sSharedPrefs
sSharedPrefs.put(packageName, packagePrefs);
}
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
//nice处理,name传null时用"null"代替
name = “null”;
}
}
//找出与文件名name关联的sp对象
sp = packagePrefs.get(name);
if (sp == null) {
//如果没找到则先根据name构建一个File的prefsFile对象
File prefsFile = getSharedPrefsFile(name);
//依据上面的File对象创建一个SharedPreferencesImpl对象的实例
sp = new SharedPreferencesImpl(prefsFile, mode);
//以key-value方式添加到packagePrefs中
packagePrefs.put(name, sp);
返回与name相关的SharedPreferencesImpl对象
return sp;
}
}
//如果不是第一次,则在3.0之前(默认具备该mode)或者mode为MULTI_PROCESS时调用reload方法
if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
//重新加载文件数据
sp.startReloadIfChangedUnexpectedly();
}
//返回SharedPreferences实例对象sp
return sp;
}
我们可以发现,上面方法中首先调运了getSharedPrefsFile来获取一个File对象,所以我们继续先来看下这个方法,具体如下:
public File getSharedPrefsFile(String name) {
//依据我们传入的文件名字符串创建一个后缀为xml的文件
return makeFilename(getPreferencesDir(), name + “.xml”);
}
private File getPreferencesDir() {
synchronized (mSync) {
if (mPreferencesDir == null) {
//获取当前app的data目录下的shared_prefs目录
mPreferencesDir = new File(getDataDirFile(), “shared_prefs”);
}
return mPreferencesDir;
}
}
可以看见,原来SharePreference文件存储路径和文件创建是这个来的。继续往下看可以发现接着调运了SharedPreferencesImpl的构造函数,至于这个构造函数用来干嘛,下面会分析。
好了,到这里我们先回过头稍微总结一下目前的源码分析结论,具体如下:
前面我们有文章分析了Android中的Context,这里又发现ContextImpl中有一个静态的ArrayMap变量sSharedPrefs。这时候你想到了啥呢?无论有多少个ContextImpl对象实例,系统都共享这一个sSharedPrefs的Map,应用启动以后首次使用SharePreference时创建,系统结束时才可能会被垃圾回收器回收,所以如果我们一个App中频繁的使用不同文件名的SharedPreferences很多时这个Map就会很大,也即会占用移动设备宝贵的内存空间,所以说我们应用中应该尽可能少的使用不同文件名的SharedPreferences,