SP:
Unlike {@link #commit}, which writes its preferences out
* to persistent storage synchronously, {@link #apply}
* commits its changes to the in-memory
* {@link SharedPreferences} immediately but starts an
* asynchronous commit to disk and you won't be notified of
* any failures. If another editor on this
* {@link SharedPreferences} does a regular {@link #commit}
* while a {@link #apply} is still outstanding, the
* {@link #commit} will block until all async commits are
* completed as well as the commit itself.
commit 通过子线程异步写文件,但通过CountDownLatch等待子线程文件写完,所以在主线程同步等待造成卡顿。
apply 子线程异步写文件,将任务提交到线程池,建立任务队列;为了防止数据丢失,在Activity onPause,onStop生命周期中取消线程池中任务,建立同步队列,等待任务执行完毕,所以也会造成主线程阻塞。
会创建一个等待锁放到 QueuedWork 中,并将真正数据持久化封装成一个任务放到异步队列中执行,任务执行结束会释放锁。Activity onStop 以及 Service 处理 onStop,onStartCommand 时,执行 QueuedWork.waitToFinish() 等待所有的等待锁释放。
SP:xml格式,子线程,每次修改都是全量修改文件,读写需要进行两次拷贝:从磁盘读取到内核缓冲,再拷贝到用户进程空间,至少牵扯到两次数据拷贝;
MMKV:
1.使用mmap 将磁盘内容映射到用户空间,数据提交相当于直接操作内存,不用开启子线程。linux操作系统保护机制会同步更新内存到文件,所以不用担心 Crash造成数据丢失。读写只需要进行一次拷贝。
2.使用protobuf格式存储文件,采用二进制协议,空间和时间性能高
3.增量更新,修改插入文件尾部,无需全量写入
4.支持多进程,通过文件锁处理同步
缺点:内存映射, linux 使用分页存储,每页至少4k,数据量小的情况下浪费内存,数据多的话需要动态扩容。
SP迁移到MMKV:
1.首次使用需要异步导入SP数据。
2.hook getSharedPrefereced方法,返回MMKV .
小记:
CountDownLatch 也是线程同步机制的一种,相当于计数器。构造器中传计数器初始值,在等待线程调用countDownLatch.await(),执行线程countDownLatch.countDown()将计数器减1,直到计数器为0时唤醒等待线程。