ESS) != 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.
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
SharedPreferences
只是接口而已,我们要获取的实际上是它的实现类 SharedPreferencesImpl
。通过 getSharedPreferencesCacheLocked()
方法可以获取已经缓存的 SharedPreferencesImpl 对象和其 sp 文件。
1.3.1 getSharedPreferencesCacheLocked()
> ContextImpl.java
private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
if (sSharedPrefsCache == null) {
sSharedPrefsCache = new ArrayMap<>();
}
final String packageName = getPackageName();
ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<>();
sSharedPrefsCache.put(packageName, packagePrefs);
}
return packagePrefs;
}
sSharedPrefsCache
是一个嵌套的 ArrayMap,其定义如下:
private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
以包名为 key ,以一个存储了 sp 文件及其 SharedPreferencesImp 对象的 ArrayMap 为 value。如果存在直接返回,反之创建一个新的 ArrayMap 作为值并存入缓存。
1.3.2 checkMode()
> ContextImpl.java
private void checkMode(int mode) {
// 从 N 开始,如果使用 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE,直接抛出异常
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) {
if ((mode & MODE_WORLD_READABLE) != 0) {
throw new SecurityException("MODE_WORLD_READABLE no longer supported");
}
if ((mode & MODE_WORLD_WRITEABLE) != 0) {
throw new SecurityException("MODE_WORLD_WRITEABLE no longer supported");
}
}
}
从 Android N 开始,明确不再支持 MODE_WORLD_READABLE
和 MODE_WORLD_WRITEABLE
,再加上 MODE_MULTI_PROCESS
并不能保证线程安全,一般就使用 MODE_PRIVATE
就可以了。
1.4 SharedPreferencesImpl
如果缓存中没有对应的 SharedPreferencesImpl
对象,就得自己创建了。看一下它的构造函数:
SharedPreferencesImpl(File file, int mode) {
mFile = file; // sp 文件
mBackupFile = makeBackupFile(file); // 创建备份文件
mMode = mode;
mLoaded = false; // 标识 sp 文件是否已经加载到内存
mMap = null; // 存储 sp 文件中的键值对
mThrowable = null;
startLoadFromDisk(); // 加载数据,见 1.4.1
}
注意这里的 mMap
,它是一个 Map<String, Object>
,存储了 sp 文件中的所有键值对。所以 SharedPreferences 文件的所有数据都是存在于内存中的,既然存在于内存中,就注定它不适合存储大量数据。
1.4.1 startLoadFromDisk()
> SharedPreferencesImpl.java
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
new Thread("SharedPreferencesImpl-load") {
public void run() {
loadFromDisk(); // 异步加载。 见 1.4.2
}
}.start();
}
1.4.2 loadFromDisk()
> SharedPreferencesImpl.java
private void loadFromDisk() {
synchronized (mLock) { // 获取 mLock 锁
if (mLoaded) { // 已经加载进内存,直接返回,不再读取文件
return;
}
if (mBackupFile.exists()) { // 如果存在备份文件,直接将备份文件重命名为 sp 文件
mFile.delete();
mBackupFile.renameTo(mFile);
}
}
// Debugging
if (mFile.exists() && !mFile.canRead()) {
Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
}
Map<String, Object> map = null;
StructStat stat = null;
Throwable thrown = null;
try { // 读取 sp 文件
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(
new FileInputStream(mFile), 16 * 1024);
map = (Map<Stri