Android应用:SharedPreferences、MMKV和DataStore怎么选

一、前言

对于 Android 轻量级存储方案,SharedPreferences、MMKV和DataStore都是用来进行键值对存储的,那么在项目中该如何选用呢?

二、SharedPreferences

是 Android 中简单易用的轻量级存储方案,用来保存相关信息。

1.本质:

键值对(key-value)的方式保存数据到 xml 文件

文件路径为:/sdcard/data/data/应用程序包名/shared_prefs

适合存储一些简单的数据,提供了多种数据类型的存储,包括:int、long、string、Boolean、float、Set<String>

读取数据:通过解析 xml 文件,得到指定 key 对应的 value

不支持多进程,虽然有基于 ContentProvider 封装的实现,但是性能低下,经常导致 ANR

2.源码分析

Context.getSharedPreferences(name, mode)

SharedPreferences 是个接口,其真正实现类是 SharedPreferencesImpl。

每当调用 SharedPreferencesImpl 的构造器的时候,都会开始调用 startLoadFromDisk 方法,然后在该方法中开启一个子线程加载 xml 文件中的内容,最后将 xml 中的内容全部加载到 mMap中

当 xml 中数据过大时,将xml一次性加载到内存中,就会导致内存占用过高,可能出现OOM

SharedPreferencesImpl.getString()

        在获取之前使用了synchronized同步锁,如果 sp 还没加载完毕,主线程会一直阻塞在那里,直到加载 sp 的子线程加载完成     

        当数据很大时,可能会导致卡顿。

Editor.commit/apply()

每次调用 edit 方法都会创建一个 Editor 对象,造成额外的内存占用

调用频繁的场景,在多次 put 之后再统一进行 commit/apply,也就是一次更新多个键值对,只进行一次 IO 操作

commit/apply 引发的ANR问题

  • commit 是同步地提交到硬件磁盘,如果在主线程中提交会阻塞线程,影响后续的操作,可能导致 ANR
  • apply 是将修改数据提交到内存,然后异步提交到硬件磁盘,没有返回值,异步操作不会阻塞调用的线程,但是如果写入任务比较耗时,会阻塞住主线程

3.卡顿原理

commit 造成的卡顿原理

commit真正的实现类是SharedPreferencesImpl,在这个类中的commit方法中。

通过enqueueDiskWrite进行写入任务,然后阻塞调用commit的线程,让调用线程处于等待状态,等写入任务完成之后就唤起调用commit的线程,如果实在主线程中执行的commit,就会阻塞主线程,如果写入任务很多,比较耗时,阻塞时间过长,可能导致ANR。

所以不要在主线程执行写入文件的操作

apply造成的卡顿

        在apply方法中,通过enqueueDiskWrite进行写入任务,但是它最后一个参数不为空,是一个postWriteRunnable回调,因此这里是一个异步任务,这个异步任务的执行是在QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit)中

QueuedWork:

        QueuedWork是Android系统提供的一个执行异步任务的工具类

内部的实现逻辑的就是创建一个HandlerThread作为工作线程,然后QueuedWorkHandler和这个HandlerThread进行管理,每当有任务就添加进来,在这个异步线程中执行,线程名为:”queued-work-looper“

       每次调用queue方法,都忘sWork 添加一个任务,它是一个LinkedList,这个队列中数据最终在queued-work-looper 线程中依次得到执行

        apply写入操作是在异步线程中执行,不会导致主线程卡顿

        但是如果异步任务执行时间过长,当ActvityThread执行了handleStopActivity()、handleServiceArgs()、handlePauseActivity() 等方法的时候都会调用QueuedWork.waitToFinish(),而此方法中会在异步任务执行完成前一直阻塞主线程,造成卡顿

        也就是说虽然apply写入操作是在异步线程中执行,但是如果要销毁的时候就会等待异步线程执行完成,导致卡顿

SharedPreferences 获取数据造成的卡顿

        数据加载是异步执行的,开启了一个子线程去执行,但是在get数据的时候,会调用awaitLoadedLocked 这个方法,当数据没有加载完,就让调用的线程处于等待中,导致阻塞

4.总结

  • 不支持多进程
  • 会导致卡顿、ANR
  • 同步比较耗时
  • 异步无法回调

三、MMKV

MMKV是微信开源出来的,起初是为了在文字显示之前先把它记录到磁盘,如果文字显示失败导致程序崩溃,就可以将它从磁盘里恢复

1.原理:

        基于内存映射文件的方式实现的

        使用mmap方式进行内存映射的key-value组件,底层使用protobuf实现,通过将文件映射到内存中,从而使得读取和写入操作都可以在内存中进行,避免了频繁的IO操作,写入性能是极高的

2.优点

  • 极高的同步写入磁盘的性能,稳定性强
  • 支持多进程

3.缺点

  • 丢数据,如果程序崩溃,或者断电关机等异常情况磁盘里的文件就会写入不完整的形式保留

四、DataStore

DataStore 被创造出来的目标就是替代 SharedPreferences

  • 异步操作:提供异步的读写操作,避免阻塞主线程;
  • 类型安全:支持使用协议缓冲区(Protocol Buffers)来定义数据模型。在编译时进行检测;
  • 支持多种数据类型:支持存储不同类型的数据,包括原始类型、对象或自定义类;
  • 数据一致性:提供一致性和安全性保证,保证在多个写入操作中的数据一致性;
  • 流式数据访问:支持使用流(Flow)来访问数据,使得可以轻松地观察数据的变化并进行相应的更新。

  • 19
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SharedPreferencesAndroid中轻量级的数据存储方式之一,用于存储一些简单的键值对数据并进行读取。以下是在Android Studio中使用SharedPreferences进行数据存储和读取的步骤: 1. 创建SharedPreferences对象 在Activity中,我们可以通过以下代码创建SharedPreferences对象: ```java SharedPreferences preferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE); ``` 其中,第一个参数是SharedPreferences的名称,第二个参数是访问模式,MODE_PRIVATE表示只有当前应用程序可以访问该SharedPreferences。 2. 存储数据 使用SharedPreferences存储数据可以使用其Editor对象,示例代码如下: ```java SharedPreferences preferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putString("key", "value"); editor.apply(); ``` 其中,putString()方法用于存储字符串类型的数据,第一个参数是键,第二个参数是值。apply()方法用于提交修改。 3. 读取数据 使用SharedPreferences读取数据可以通过以下代码: ```java SharedPreferences preferences = getSharedPreferences("my_prefs", Context.MODE_PRIVATE); String value = preferences.getString("key", ""); ``` 其中,getString()方法用于读取字符串类型的数据,第一个参数是键,第二个参数是默认值。 以上就是在Android Studio中使用SharedPreferences进行数据存储和读取的基本步骤。需要注意的是,SharedPreferences适合存储一些简单的键值对数据,对于复杂数据建议使用SQLite数据库等其他数据存储方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值