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

    原文作者:普通的程序员
    原文地址: https://www.jianshu.com/p/f29b146b76dd
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞