Android SharedPreference源码阅读
SharedPreferences是一个interface
Editor是SP的内部interface
真正的实现类是SharedPreferencesImpl类和EditorImpl类
在ContextImpl类里可以看到其真实调用
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
checkMode(mode);
if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
if (isCredentialProtectedStorage()
&& !getSystemService(StorageManager.class).isUserKeyUnlocked(
UserHandle.myUserId())
&& !isBuggy()) {
throw new IllegalStateException("SharedPreferences in credential encrypted "
+ "storage are not available until after user is unlocked");
}
}
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
sp = cache.get(file);
if (sp == null) {
sp = new SharedPreferencesImpl(file, mode);
cache.put(file, 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.
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
其构造方法SharedPreferencesImpl(file, mode)
内部调用startLoadFromDisk();
该方法再开启线程进行loadFromDisk();
源码阅读参考
https://www.jianshu.com/p/31ffd0498496
在SP解析xml时,会遇到null kv的问题
https://www.jianshu.com/p/796ba810aa46
根源在于4.X的sdk里允许了往SharedPreferences里写入key为null的值而不允许取出来,并且在之前没有从磁盘中读取到值到内存中,在对值进行copy的时候就"丢失",从而导致写入进去的新文件里没有以前的旧值,由此现象上来看好像是某些值被删除了,但实际上确切的说应该是被空值所覆盖。
滥用SP可能造成的问题
http://weishu.me/2016/10/13/sharedpreference-advices/?hmsr=toutiao.io
大SP文件的读取:
1.第一次从sp中获取值的时候,有可能阻塞主线程,使界面卡顿、掉帧;
2.解析sp的时候会产生大量的临时对象,导致频繁GC,引起界面卡顿;
3.这些key和value会永远存在于内存之中,占用大量内存。
卡顿是因为下面这个方法,每一个getXX,都会一直在wait,
在load没有结束前,mLoaded会一直是false
private void awaitLoadedLocked() {
while (!mLoaded) {
try {
wait();
} catch (InterruptedException unused) {
}
}
}
【SharedPreferences.Editor 有 apply 和 commit 两个方法用于提交数据编辑,这两个方法的区别在于:
apply没有返回值而commit返回boolean表明修改是否提交成功
apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后在操作,从而降低了效率。而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
apply方法不会提示任何失败的提示。
由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。】
--来源https://www.jianshu.com/p/13f26d68b02e
整个apply分为三个步骤:
- 通过commitToMemory写入到内存中
- 通过enqueueDiskWrite写入到磁盘中
- 通知监听者
第二步的写操作SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable)的内部实现稍有不同。
API 25:
QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
为核心线程只有一个线程的线程池;
API 26:
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
为handler
跨进程的支持
在构造函数就可以看到,实际上就是重新读取一次xml文件,所以对于多进程的支持并不是很好。
Google建议使用contentProvider
https://www.jianshu.com/p/875d13458538
SharedPreferences 线程阻塞及吃内存原因面试题
https://mp.weixin.qq.com/s/VRUXQvmEEveNVR-DQD5w9Q
坑点分析
主要是一个flag值
https://www.jianshu.com/p/40e42da910e2