SharedPreferences源码笔记

  1. SharedPreferencesImpl: SharedPreferences的真正实现.

    • 总体的设计思想是构造时直接从文件将信息IO读入内存, 在IO就绪前的getXXX()全部wait, 在IO结束以后notifyAll()放行.
    • 对于修改的提交提供了apply(异步)和commit(同步), 两者其实流程基本一致,都是根据当前的修改信息构造一个MemoryCommitResult来进行调度IO(QueuedWork提供了singleThreadExecutor)和结果投递, 唯一不同的是commit()在post任务以后会直接在当前线程进行wait. 而apply()则是基本不会等待, 而由QueuedWork的waitToFinish()方法的调用者(ActivityThread)来保证在某些时间点等待task的完成.
    • Editor的修改在apply()/commit()生成MemoryCommitResult时就直接将改动反映在了内存中,这样getXXX()就不必等待必须改动IO完成直接读取内存中信息即可
    • writeTask的提交是队列式的(SingleThreadExecutor来保证),为了保证IO顺序.
  2. 构造时就调用了startLoadFromDisk(), 其流程很简单:

    • 同步(SharedPreferencesImpl对象)将 mLoaded设为false.
    • 启动一个线程,线程的run()里同步(SharedPreferencesImpl)调用loadFromDiskLocked().
  3. loadFromDiskLocked():

    • check是否已经load过了, loaded过直接return.
    • Libcore.os.stat(…)获得要读取文件的信息, 如果可读,那么开读.
    • 读取到的信息利用XmlUtils.readMapXml(str)构造为一个map.
    • 该map被设置为mMap. 如果解析失败,会new一个空的HashMap.
    • 最后调用notifyAll()来通知所有wait的请求.
    • mMap类型是Map “<”String, Object”>”().
  4. getXXX():

    • 所有调用都会先同步SharedPreferencesImpl对象,然后执行 awaitLoadedLocked(), 在load完notifyAll()以后从生成mMap中取出元素.
    • getXXX()对从Map获取的元素都是强转.
  5. awaitLoadedLocked():

    • 一个检查mLoaded的while循环里的wait().
  6. contains(String key):和getXXX()一样,要等待load的完成.

  7. edit()返回一个Editor(new EditorImpl()), 也会等待load的完成.

  8. MemoryCommitResult:

    • 用于返回commit的结果.
    • boolean changesMade: 是否有change.
    • List”<”String”>” keysModified: 有哪些key被change了
    • Set”<”OnSharedPreferenceChangeListener”>” listeners: 对change的listener.
    • Map”<”?, ?”>” mapToWriteToDisk: 要写进硬盘的Map信息
    • final CountDownLatch writtenToDiskLatch = new CountDownLatch(1):
    • volatile boolean writeToDiskResult = false 是否写进了Disk.
    • setDiskWriteResult(boolean result): 标记commit的结果, 将writtenToDiskLatch进行countDown()以使得阻塞的操作进行.
  9. enqueueDiskWrite(final MemoryCommitResult mcr,
    final Runnable postWriteRunnable):

    • new一个runnable内部的操作是先同步mWritingToDiskLock将承载了写入信息的mcr写入到Disk中, 然后同步SharedPreferencesImpl.this将mDiskWrites**InFlight**数量-1, 组后执行**postWrite**Runnable里封装的操作.
    • 根据是否有postWriteRunnable来区分是不是isFromSyncCommit.
    • 如果是isFromSyncCommit, 那么检查mDiskWritesInFlight == 1, 如果成立,那么说明当前inflight的diskWriteTask只有目前这一个,直接执行writeToDiskRunnable.run()然后返回.
    • 否则会将writeToDiskRunnable放入到QueuedWork类中的singleThreadExecutor()进行执行(不是实时的,也不在当前线程).
  10. createFileOutputStream(File file):

    • 会在File不存在的情况下为其创建file并赋予相应权限.
    • 但是注意,如果上级目录都不存在,那么无能为力.
  11. writeToFile(MemoryCommitResult mcr):

    • 首先check file是否存在.
    • 如果输入的mcr标记没有做任何change, 那么显然不必浪费做一次无用的IO,直接mcr.setDiskWriteResult(true)后return.
    • 会先将File rename为mBackupFile进行写入前内容的备份.
    • 获取mFIle的FileOutputStream.
    • 利用XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str)将mcr的mapToWriteToDisk中的内容写入到FileOutputStream.
    • FileUtils.sync(str);来将修改内容从内存flush到disk.
    • 确定写入成功哟以后会删除掉mBackupFile(不需要之前的备份了)
    • 相应的mcr.setDiskWriteResult(true), 返回.
    • 其他情况视为写入失败,将当前被污染的mFile删除并且mcr.setDiskWriteResult(false)来标识失败.
  12. EditorImpl:

    • 内部维护一个mModified = Maps.newHashMap().
    • 所有的putXXX()/remove(String key)都是对mModified进行修改.
    • apply():
      • new一个MemoryCommitResult(mcr)作为对于commit结果的回传, 再new一个Runnbale(awaitCommit)里面就是执行对此MemoryCommitResult的mcr.writtenToDiskLatch(一个countDownLock).await().其将一直等待mcr的setDiskWriteResult(result)被调用完毕,无论成功失败.
      • 然后将awaitCommit加入到QueuedWork. 在QueueWork中加入此Task的目的是为了确保在某些时机前,相应的Task确保被执行完毕,详情可见QueuedWork中add(Runnable finisher)的注释, ActivityThread中有几处使用了QueuedWork.waitToFinish()的地方来确保在这个时候,QueueWork中add的finisher所标识的Task被执行完毕, 在这里就是被提交的writeToDisk的task被执行完毕
      • 再new一个Runnable(postWriteRunnable), 其中run内容是awaitCommit.run(); + QueuedWork.remove(awaitCommit);
      • 将mcr 和 postWriteRunnable enqueueDiskWrite(…). 这一步将会执行之前mcr的setDiskWriteResult(result)或者将其放入一个executor来进行调用执行.
      • notifyListeners(mcr).
    • commit(): 利用commitToMemory()或的一个基于当前editor的mModifies的MemoryCommitResult, 然后将mcr进行enqueueDiskWrite(…);postRunnable填为null. 然后直接mcr.writtenToDiskLatch.await()进行同步等待,这就是commit()是同步的原因.
    • commitToMemory(): 用于根据当前Editor的mModified来生成一个封装了ChangeInfo的MemoryCommitResult对象, 该对象就像上面说的,会被调度进行将change写入disk的操作以及结果的投递.
      • 所有的操作都要同步SharedPreferencesImpl.this.
      • 如果当前已经有inflight的diskWriteTask, 那么会将mMap进行一次copy以防止可能的冲突(这么做的原因是, 在没有WriteTaskInflight的情况下,mcr的mapToWriteToDisk会直接赋予mMap这个引用,这就意味这mMap在write操作真正进行的时候也会被用到,而如果这时候另外一个MemoryCommitResult在生成时也使用了mMap引用, 那么在其对mMap的操作就会反映到正在write到Disk用到的Map上,造成了冲突, 因此,在这种情况下,需要将mMap原来指向的对象重新copy生成一份新的, 并命令mMap重新指向它, 这样对这个copy的修改就不会反映到前一个MemoryCommitResult writeToDisk时用到的Map了).
      • 结合mModified和mMap进行比对,确定mcr的changesMade以及对mMap进行相应的修改, 这样修改就直接反映在内存中了,后续的getXXX操作就直接从内存中的Map中取出信息而不必再等待IO结束以后的重新读取. 然后将mModified清空.
      • 最后返回生成的mcr.
      • mModified中对于remove的操作的表示比较独特,其key值会被保存,而value则被设置为Editor本身,这样在对mModifies的check中,发现这种情况的key, 就会被视为要删除这个key.
  13. QueuedWork中add(Runnable finisher)的注释:

    • 添加一个runnable来对之前提交过的defer operation进行finish/wait.
    • 一般来说,在这里添加的task是不会被执行的,使用者会将其放入然后remove,只有在waitToFinish()被调用时才会挨个的将加入的task进行run以实现收尾或者等待的目的.
  14. QueuedWork中的singleThreadExecutor()返回的sSingleThreadExecutor是lazy-init并且整个进程共享的.

    原文作者:fyfcauc
    原文地址: https://blog.csdn.net/fyfcauc/article/details/47613003
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞