广告
返回顶部
首页 > 资讯 > 精选 >SharedPreference初始化源码分析
  • 713
分享到

SharedPreference初始化源码分析

2023-07-05 19:07:38 713人浏览 安东尼
摘要

本篇内容介绍了“SharedPreference初始化源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!初始化sp 内部将数据放到 xm

本篇内容介绍了“SharedPreference初始化源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

初始化

sp 内部将数据放到 xml 文件中,加载时首先会将硬盘中文件读取到内存中,这样加快了访问速度

这次从源码开始,看看里面具体做了什么

    // 初始化    SharedPreferencesImpl(File file, int mode) {        // 文件        mFile = file;        //备份文件 .bak 结尾,看看什么时候排上作用,比如恢复数据        mBackupFile = makeBackupFile(file);        mMode = mode;        mLoaded = false;        mMap = null;        mThrowable = null;        // 从硬盘中读取        startLoadFromDisk();    }

硬盘中读取文件开了新线程,主要将文件中的内容,转换为Map

    private void loadFromDisk() {        synchronized (mLock) {            if (mLoaded) {                return;            }            // 存在备份文件,删除 file,为什么            if (mBackupFile.exists()) {                mFile.delete();                mBackupFile.renameTo(mFile);            }        }        Map<String, Object> map = null;        StructStat stat = null;        Throwable thrown = null;            stat = Os.stat(mFile.getPath());                // 读取流                BufferedInputStream str = null;                try {                    str = new BufferedInputStream(                            new FileInputStream(mFile), 16 * 1024);                    // 转为 map                                map = (Map<String, Object>) XmlUtils.readMapXml(str);                } catch (Exception e) {                    Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);                } finally {                    // 关闭流                    IoUtils.closeQuietly(str);                }    }

流程很简单,就是读取硬盘,转换为一个 Map

apply,commit 区别

首先 apply,commit 分别是异步/同步的写入操作,它们都会先写入内存中,也就是更新 Map,不同在于写入到硬盘的时机不同

  • commit 先看 commit 做了什么 ,commit 方法将返回一个布尔值,表示结果

@Override        public boolean commit() {            // 先提交到内存中            MemoryCommitResult mcr = commitToMemory();            // 执行硬盘中的更新            SharedPreferencesImpl.this.enqueueDiskWrite(                mcr, null );            try {                mcr.writtenToDiskLatch.await();            } catch (InterruptedException e) {                // 提交异常,返回 false                return false;            }            // 通知监听            notifyListeners(mcr);            // 返回结果            return mcr.writeToDiskResult;        }
  • apply

        @Override        public void apply() {            final long startTime = System.currentTimeMillis();            // 都是一样的,先写到内存            final MemoryCommitResult mcr = commitToMemory();            final Runnable awaitCommit = new Runnable() {                    @Override                    public void run() {                        //                         mcr.writtenToDiskLatch.await();                    }                };            // 往 sFinishers 队列中添加,等待执行            QueuedWork.addFinisher(awaitCommit);            // 在写完后执行 postWriteRunnable            Runnable postWriteRunnable = new Runnable() {                    @Override                    public void run() {                        // 执行 awaitCommit                        awaitCommit.run();                        // sFinishers 队列中移除                        QueuedWork.removeFinisher(awaitCommit);                    }                };            // 写入硬盘            SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);        }

硬盘中是如何更新的呢

    private void enqueueDiskWrite(final MemoryCommitResult mcr,                                  final Runnable postWriteRunnable) {        final boolean isFromSyncCommit = (postWriteRunnable == null);        // 创建 Runnable 对象        final Runnable writeToDiskRunnable = new Runnable() {                @Override                public void run() {                    // 写到文件,这里的是 mWritingToDiskLock 对象                    synchronized (mWritingToDiskLock) {                        writeToFile(mcr, isFromSyncCommit);                    }                    synchronized (mLock) {                        mDiskWritesInFlight--;                    }                    // 执行 postWriteRunnable, commit 这里为 null                    // apply 时不为i而空                    if (postWriteRunnable != null) {                        postWriteRunnable.run();                    }                }            };        // Typical #commit() path with fewer allocations, doing a write on        // the current thread.        // 是否为同步提交        // 根据 postWriteRunnable 是否为空, commit 这里为 true        // apply         if (isFromSyncCommit) {            boolean wasEmpty = false;            synchronized (mLock) {                wasEmpty = mDiskWritesInFlight == 1;            }            if (wasEmpty) {                writeToDiskRunnable.run();                return;            }        }        // 放到队列中执行,内部是一个 HandlerThread,按照队列逐个执行任务        QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);    }

这里用队列来放任务,应该是要应对多个 commit 情况,这里将所有 commit 往队列里面放,放完后就会执行硬盘的写,apply 也会调用到这里

   public static void queue(Runnable work, boolean shouldDelay) {        Handler handler = getHandler();        synchronized (sLock) {            // 添加到 sWork 队列中            sWork.add(work);            // 异步 apply 走这个            if (shouldDelay && sCanDelay) {                handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);            } else {            // 同步 commit 走这个                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);            }        }    }

apply 的硬盘写入,需要等待 Activity.onPause() 等时机才会执行

读取

读取比写入就简单很多了

  • 先查看是否从硬盘加载到了内存,没有就先去加载

  • 从内存中读取

 public String getString(String key, @Nullable String defValue) {        synchronized (mLock) {            // 检查是否从硬盘加载到了内存,没有就先去加载            awaitLoadedLocked();            String v = (String)mMap.get(key);            return v != null ? v : defValue;        }    }

如何保证线程安全

通过 sync 加对象锁,内存读写都是用的同一把锁,所以读写都是线程安全的

数据恢复

存在备份机制

  • 对文件进行写入操作,写入成功时,则将备份文件删除

  • 如果写入失败,之后重新初始化时,就使用备份文件恢复

SP 与 ANR

由于 Activity.onPause 会执行 apply 的数据落盘,里面是有等待锁的,如果时间太长就会 ANR

“SharedPreference初始化源码分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: SharedPreference初始化源码分析

本文链接: https://www.lsjlt.com/news/354532.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SharedPreference初始化源码分析
    本篇内容介绍了“SharedPreference初始化源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!初始化sp 内部将数据放到 xm...
    99+
    2023-07-05
  • initoutputstream初始化输出流源码分析
    目录正文音频 OutputStream初始化init_output_stream() 函数流程图正文 init_output_stream() 是一个公...
    99+
    2022-11-13
    init output stream初始化输出流 init output stream
  • spring初始化源码代码浅析
    目录前言1、refresh()简介2、关键代码跟踪2.1、obtainFreshBeanFactory()代码分析2.2、invokeBeanFactoryPostProcessor...
    99+
    2023-05-18
    spring初始化源码 spring初始化代码 spring 初始化
  • tdesignvue初始化组件源码解析
    目录前言源码脚本的入口创建目录内容写入删除目录删除导入语句总结前言 Tdesign-vue 是一由腾讯开源的 Vue.js 组件库。我们知道,这些大型的组件库业务覆盖面很广,基本都包...
    99+
    2022-12-21
    tdesign vue初始化组件 tdesign vue
  • 分析Linux内核调度器源码之初始化
    目录一、导语二、调度器的基本概念2.1、运行队列(rq)2.2、调度类(sched_class)2.3、调度域(sched_domain)2.4、调度组(sched_group)2.5、根域(root_domain)...
    99+
    2022-06-03
    Linux 内核源码 调度器 初始化
  • Vue3源码分析组件挂载初始化props与slots
    目录前情提要初始化组件(1).setupComponent(2).initProps(3).initSlots额外内容总结前情提要 上文我们分析了挂载组件主要调用了三个函数: cre...
    99+
    2022-11-13
    Vue3组件挂载初始化 Vue3 初始化props slots
  • java初始化分析
    关于初始化的一点体会 [@more@]class Egg2 {static int i=5; int j=5; static//父类静态变量初始化块首先被执行,在main方法之前。 { System.out.println("superCl...
    99+
    2023-06-03
  • Netty源码分析NioEventLoop初始化线程选择器创建
    前文传送门:NioEventLoop创建 初始化线程选择器 回到上一小节的MultithreadEventExecutorGroup类的构造方法: protected Multith...
    99+
    2022-11-13
  • Netty启动流程服务端channel初始化源码分析
    目录服务端channel初始化回顾上一小节initAndRegister()方法init(Channel)方法前文传送门 Netty分布式server启动流程 服务端cha...
    99+
    2022-11-13
  • python深度学习tensorflow参数初始化initializer源码分析
    本篇内容介绍了“python深度学习tensorflow参数初始化initializer源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成...
    99+
    2023-07-06
  • Netty分布式客户端接入流程初始化源码分析
    目录前文概述:第一节:初始化NioSockectChannelConfig创建channel跟到其父类DefaultChannelConfig的构造方法中再回到AdaptiveRec...
    99+
    2022-11-13
  • 详解从源码分析tomcat如何调用Servlet的初始化
    目录引言一、代码启动tomcat二、tomcat框架三、创建容器(addWebapp())3.1 方法 调用流程图3.2 源码分析四、启动容器(tomcat.start())4.1、...
    99+
    2022-11-12
  • Netty分布式Server启动流程服务端初始化源码分析
    目录第一节:服务端初始化group方法初始化成员变量初始化客户端Handler第一节:服务端初始化 首先看下在我们用户代码中netty的使用最简单的一个demo: //创建boss和...
    99+
    2022-11-13
  • Spring源码解析容器初始化构造方法
    目录前言构造方法前言 Spring框架被广泛应用于我们的日常工作中,但是很长时间以来我都是只会使用,不懂它的作用原理。通过最近一段时间的阅读源码,个人发现通过阅读源码,能够帮助我们了...
    99+
    2022-11-13
  • 如何进行SpringMVC源码中的初始化源码
    如何进行SpringMVC源码中的初始化源码,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。所有Java的MVC框架都是基于servlet的,SpringMVC也不例外。它提供核...
    99+
    2023-06-02
  • wifidog 源码初分析(3)
    上一篇分析了 接入设备 在接入路由器,并发起首次 HTTP/80 请求到路由器上时,wifidog 是如何将此 HTTP 请求重定向至 auth-server 的流程。 之后 接入设备 的浏览器接收到 wifidog 返回的 302 重定向...
    99+
    2023-01-31
    源码 wifidog
  • Vue源码学习之数据初始化
    目录初始化数据创建Vue实例构造函数扩展方法初始化状态调用initData方法对数据进行代理初始化数据 环境搭建:菜鸟学Vue源码第一步之rollup环境搭建步 响应式数据的核心就是...
    99+
    2022-11-13
  • vue-router初始化的示例分析
    这篇文章主要为大家展示了“vue-router初始化的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“vue-router初始化的示例分析”这篇文章吧。v...
    99+
    2022-10-19
  • SpringMVC初始化流程实例分析
    本文小编为大家详细介绍“SpringMVC初始化流程实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“SpringMVC初始化流程实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.HttpServl...
    99+
    2023-07-02
  • 【Spring6源码・IOC】Bean的初始化 - 终结篇
    前面两篇,我们着重讲解了一下《BeanDefinition的加载》和《bean的实例化》。 这一篇我们来讲解一下bean的初始化。 我们这里的案例依旧是以SpringBoot3.0、JDK17为前提...
    99+
    2023-09-11
    缓存 spring java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作