前言:本文是自己对SharedPreferences 存入数据过程源码的分析及总结,欢迎指正。
一、SharedPreferences存入数据源码解析
1.SharedPreferences 存入数据过程:
获取 SharedPreferences 对象
通过 Editor 获取编辑器对象
以键值对的形式写入数据
提交修改
//获取SharedPreferences对象
//第一种获取对象方法 -> 通过PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
//第二种获取对象方法 ->通过上下文 Context.getSharedPreferences("指定的文件名称",文件操作模式); 操作模式不做赘述,建议度娘
SharedPreferences mSharedPreferences = getApplicationContext().getSharedPreferences("er_liang",Context.MODE_PRIVATE);
首先来看下这两种获取对象方式的源码
//第一种在PreferenceManager.java文件下
public static SharedPreferences getDefaultSharedPreferences(Context context) {
return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
getDefaultSharedPreferencesMode());
}
//可以看到第一种通过PreferenceManager获取SharedPreferences实例
//这种方法只是将Context().getSharedPreferences("name",MODE)进行了一次封装,将其作为返回值
//通俗易懂的说 就是这种调用方式 是调用了一个”默认“的SharedPreferences对象,文件名称以及操作模式是默认的。
//默认的文件名为上下文的包名+"_preferences"
public static String getDefaultSharedPreferencesName(Context context) {
return context.getPackageName() + "_preferences";
}
//默认文件操作模式为MODE_PRIVATE
private static int getDefaultSharedPreferencesMode() {
return Context.MODE_PRIVATE;
}
//第二种获取实例方式 Context.java -> 通过上下文Context.getSharedPreferences(参数略)
public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
//其在ContextImpl.java中实现
public SharedPreferences getSharedPreferences(File file, int mode) {
SharedPreferencesImpl sp;//sp -> SharedPreferences的实现类 (不要忘了SharedPreferences是个接口)
synchronized (ContextImpl.class) {
final ArrayMap cache = getSharedPreferencesCacheLocked();//根据对应的包名获取preferences的缓存
sp = cache.get(file);//若file在缓存中有,那就取出来
if (sp == null) { //如果缓存中没有,就new 一个sp对象
checkMode(mode);
if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
//此部分判断是为了防止未解锁时就能使用存储
if (isCredentialProtectedStorage()
&& !getSystemService(UserManager.class)
.isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
throw new IllegalStateException("SharedPreferences in credential encrypted "
+ "storage are not available until after user is unlocked");
}
}
sp = new SharedPreferencesImpl(file, mode);//new 的对象在这
cache.put(file, sp);//将对应文件名和sp存入缓存中
return sp;
}
}
//MODE_MULTI_PROCESS多进程模式或者SDK<11时,就会重新从磁盘加载文件
//不过多进程模式现在已经不用了 现在有另一个老大哥帮忙协调多进程访问 -> ContentProvider
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.
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
//可能到这就开始迷糊了...到底是如何将数据存入缓存的呢?
//继续看SharedPreferences的实现类 -> SharedPreferencesImpl
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);//生成.bak文件
mMode = mode;//文件访问模式
mLoaded = false;//标志位,用于判断数据从磁盘加载到内存是否完成
mMap = null;//保存在内存中SharedPreferences对象的Map容器
mThrowable = null;
startLoadFromDisk();//从磁盘中加载文件到内存中
}
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
new Thread("SharedPreferencesImpl-load") {
public void run() {
loadFromDisk();//开启了一个线程去从磁盘中加载文件到内存中
}
}.start();
}
//具体的存入逻辑
private void loadFromDisk() {
synchronized (mLock) {
if (mLoaded) { //如果mLoaded为true代表存入完成->退出
return;
}
if (mBackupFile.exists()) {
mFile.delete();
mBackupFile.renameTo(mFile);
}
}
// Debugging
if (mFile.exists() && !mFile.canRead()) {
//如果没有读取权限
Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
}
Map map = null;
StructStat stat = null;
Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
//将SharePreferences文件以流的形式读出来
str = new BufferedInputStream(new FileInputStream(mFile), 16 * 1024);
map = (Map) XmlUtils.readMapXml(str);
//读取xml中的内容 构造出一个map
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
// An errno exception means the stat failed. Treat as empty/non-existing by
// ignoring.
} catch (Throwable t) {
thrown = t;
}
synchronized (mLock) {
mLoaded = true;
mThrowable = thrown;
// It's important that we always signal waiters, even if we'll make
// them fail with an exception. The try-finally is pretty wide, but
// better safe than sorry.
try {
if (thrown == null) {
if (map != null) {
mMap = map;//将上方构建的map赋值给mMap
mStatTimestamp = stat.st_mtim;//获取时间戳
mStatSize = stat.st_size;//获取文件大小
} else {
mMap = new HashMap<>();
}
}
// In case of a thrown exception, we retain the old map. That allows
// any open editors to commit and store updates.
} catch (Throwable t) {
mThrowable = t;
} finally {
mLock.notifyAll();
//通知mLock锁住的数据已经加载完成,数据可以被使用
//mLock 在SharedPreferencesImpl.java最上边定义了
//private final Object mLock = new Object();
}
}
}
首次自己分析源码,有很多不足之处欢迎指正。
PS:博客不怎么看评论,欢迎移步公众号 -> "二两仙气儿" 我自己的一些demo和总结 都会放在公众号上。