带着问题学习

  1. SharedPreference 是线程安全的吗?
  1. SharedPreference 的 apply 和 commit 有什么区别?
  1. SharedPreference 是否会阻塞主线程?
  1. SharedPreference 的文件是存储在哪的?
  1. SharedPreference 与应用程序之间是一对一的吗?

简介

SharedPreference 是一种以 键值对 形式存储数据的存储方式,在使用的时候,可以指定是否将其共享,最终的 SharedPreference 文件是存储在对应应用的存储区的(具体的是 /data/data/packageName/shared_prefs),也就是说,默认其他应用是无法访问的

简单使用

  • 获取 SharedPreference 实例
    • 获取 sharedPreferences 的编辑对象
      • 利用 editor,存储相关数据
        • 把结果提交,也就是写到文件中

          源码解析

          SharedPreferencesMode

          在使用 getSharedPreferences() 时,需要传入两个参数,一个是 filename,也就是存储在本地中的 xml 文件的文件名,而 mode 是指存储的权限,对于第一个参数没什么好说的,所以,来看看 mode 有哪些取值先
          可以看到有四种取值,其中 MODE_MULTI_PROCESS 的作用是表明该 SharedPreference 文件允许被多个进程进行访问,而一般情况下,当调用了 getSharedPreferences() 方法时,SharedPreference 的对象就会与调用它的应用进程绑定,此时如果有其他应用进程来访问该 SharedPreference 文件,是会报错的,也就是默认情况下,SharedPreference 和调用的进程之间是一对一的关系。

          查看 getSharedPreferences()

          直接点进 getSharedPreferences() 的实现,发现其是调用关系如下:
          原来是调用了 Context 实现的 getSharedPreferences() 方法,那我们就跟过去看看 Context 中是怎么实现的 getSharedPreferences() :
          发现 Context 是一个抽象类,而 getSharedPreferences() 也是一个抽象方法,因此查看 Context 的实现类 ContextImpl,下面来看看 ContextImpl 中的实现:
          到这里,就看明白了 SharedPreferences 的实例是怎么创建以及获取的了,下面来看看 SharedPreferences 真正的实现类 SharedPreferencesImpl 是怎么实现的

          SharedPreferencesImpl

          该类是真正提供 SharedPreferences 实现的类,也就是说,SharedPreferences 的各种操作,都是由该类实现并提供的
          看完了 SharedPreferencesImpl 构造时的流程,可以知道,SharedPreferencesImpl 在一构造出来的时候,就会启动一个子线程,从磁盘存储中加载相应的 SharedPreferences 文件到内存中
          那么接下来就来看看 SharedPreferencesImpl 中提供的各种操作方法的实现是怎么样的:
          • get 系列方法以 getString() 方法为例,其他都类似
          • edit
            到了这里,就到了我们在需要修改 SharedPreferences 中值时候需要用到的 Editor 的实现类 EditorImpl 了,接下来就来看看相关的方法是怎么实现的

            EditorImpl

            • 点进源码发现,该类还是 SharedPreferencesImpl 的一个内部类,接下来就来看看相关的方法是怎么实现的
            • put 系列方法,以 putString() 方法为例,其他都类似
            在上面的代码中看到,put 系列的操作,并没有真正把我们的修改写入到相应的文件中,而是保存在内存中,按照我们的使用步骤,在修改完值之后,还要调用 apply 或 commit 方法把最终的修改保存到文件中
            • apply 方法
            • commit 方法
            从上面 commit 和 apply 的方法实现中,可以看出,两者都调用了 SharedPreferencesImpl.this.enqueueDiskWrite() 方法,来把写入磁盘的任务加入队列,下面来看看 SharedPreferencesImpl.this.enqueueDiskWrite() 的具体实现是什么

            总结

            • 从上面 apply 和 commit 的方法可以看出来,apply 是在子线程中等待写入磁盘的任务结束,而 commit 是在运行线程中等待写入磁盘的任务结束,也就是说 apply 是异步写入磁盘的,commit 是同步写入磁盘的,因此如果写入任务过重的话, commit 可能会导致 ANR。而且 commit 方法是有返回值的,而 apply 没有,这也很好理解,因为很难让异步的方法,返回其运行结果,还不阻塞住线程
            • 对于 commit 的把数据写进磁盘的操作来说,不一定是只执行在主线程,也有可能是执行在子线程,具体要看其前面的 apply 是否已经执行完毕

            问题回答

            1. SharedPreference 是线程安全的吗?
                • 是的,所有操作都有 syschronized 修饰
            1. SharedPreference 的 apply 和 commit 有什么不同?
                • apply 是异步的,是启动子线程来执行把结果写入磁盘的操作
                • commit 是同步的,一般是在主线程中执行把结果写入磁盘的操作
            1. SharedPreference 是否会阻塞主线程?
                • 使用 apply 就不会,使用 commit 一般都会,也有特例
            1. SharedPreference 的文件是存储在哪的?
                • 存储在应用的存储区
            1. SharedPreference 与应用程序之间是一对一的吗?
                • 不是,一个应用可以持有多个 SharedPreference,但如果没有特别指定 SharedPreference 的模式为 MODE_MULTI_PROCESS,则一个 SharedPreference 只能被一个应用进程持有
            Android 横竖屏适配Handler 内存泄漏