Android存储方案对比(SharedPreferences 、 MMKV 、 DataStore)

简介:本文介绍了Android开发中常用的键值对存储方案,包括SharedPreferences、MMKV和DataStore,并且对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。

 在Android开发中,键值对存储(Key-Value Strorage)是一种经常用到的轻量级数据存储方案。它用于保存一些简单的配置数据或状态信息,例如用户设置、缓存数据等。

常见的键值对存储方案

SharedPreferences

SharedPreferences是Android系统提供的一种轻量级的持久化存储类,使用键值对的形式保存数据;可以存储的数据类型包括String、int、boolean、float和long;简单易用,但在高并发写操作下性能较差,会造成主线程阻塞问题。

SharedPreferences sharedPreferences = getSharedPreferences("my_preferences", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("username", "John");
editor.putInt("age", 30);
editor.apply();  // apply() 异步保存数据,commit() 是同步操作

特点:

存储方式:键值对(Key-Value Pairs),数据以XML文件储存。

使用方式:通常用于存储简单的用户设置、偏好设置等,适合存储少量的数据。

线程安全:在多线程的环境中操作时,需要特别注意同步问题。SharedPreferences本身是线程安全的,但你在修改数据时,可能需要使用apply()或commit()进行操作。

性能:性能较差,尤其是当存储的数据量较大时,因为它是基于文件的,数据时以文本格式存储的,且需要频繁的磁盘IO操作。

 优点:

使用简单,不丢数据

使用非常方便,能确保数据的一致性,适合不频繁读写一些重要的数据。

缺点:

SP不能保证类型安全

获取数据的时候可能出现ClassCastException异常,因为使用相同的KEY调用put()保存不同类型的数据时会覆盖之前保存的数据类型。

SP加载的数据会一直留在内存中

使用getSharedPreferences()方法加载数据会将数据存储在静态的成员变量中,然后通过静态的ArrayMap缓存每一个SP文件,而每个SP文件内容通过Map缓存键值对数据,这样数据会一直留在内存中,浪费内存。

不支持多线程

SP不支持跨进程通信;代码中可以看到当使用多进程MODE_MULTI_PROCESS操作的时候,会重新读取SP文件内容。

    @Overridepublic SharedPreferences getSharedPreferences(File file, int mode) {SharedPreferences sp;synchronized (ContextImpl.class){final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();sp = cache.get(file);if(sp == null){checkMode(mode);if(getApplicationInfo().tragetSdkVersion >= android.os.Build.VERSION_CODES.O){if(isCredentialProtectedStorage() && !getSystemService(UserManager.class).isUserUnlockingOrUnlocked(UserHandle.myUserId())){throw new IllegalStateException("Credential protected storage is not available");}}sp = new SharedPreferencesImpl(file, mode);cache.put(file, sp);return sp;}}if((mode & Context.MODE_MULTE_PROCESS) != 0 || getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB){// If somebody else (some other process) changed the prefs// file behind our back, we reload it. This has been the historical (if undocumented) behavior.sp.startReloadIfChangedUnexpectedly();}return sp;}

读写性能差,可能引起ANR

 读取数据的时候虽然加载文件也是异步加载的,不过sp.get()方法是同步的,如果代码在它加载完成之前就去尝试读取键值对,线程就会阻塞,直到文件加载完成,此时如果在主线程操作的话,就会造成界面卡顿。

写入数据时SP可以通过apply()异步的方式来保存更改避免I/O操作所导致的主线程的耗时,但当Activity启动和关闭的时候会等待这些异步提交完成保存之后,这就相当于把异步操作转换成同步操作了,从而会导致卡顿甚至ANR。当然这些操作也是为了能保证数据安全一致而为之。

具体ANR引起原因可以参考:剖析 SharedPreference apply 引起的 ANR 问题。

MMKV

MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今在微信上使用,其性能和稳定性经过了时间的验证。近期也已移植到 Android / macOS / Win32 / POSIX 平台,一并开源。

总的来说,MMKV使用mmap内存映射文件,极大提高了读写性能,并且支持多进程读写;完全替代SharedPreferences,有一致的API使用体验;提供分布式存储、数据加密等功能。

依赖配置

implementation 'com.tencent:mmkv-static:1.2.10'

初始化和使用

import com.tencent.mmkv.MMKVclass MyApplication : Application() {override fun onCreate() {super.onCreate()MMKV.initialize(this)}
}fun saveData(key: String, value: String) {val kv = MMKV.defaultMMKV()kv.encode(key, value)
}fun getData(key: String): String? {val kv = MMKV.defaultMMKV()return kv.decodeString(key)
}
MMKV mmkv = MMKV.defaultMMKV();
mmkv.putString("username", "John");
mmkv.putInt("age", 30);
String username = mmkv.getString("username", "default_value");
int age = mmkv.getInt("age", 0);

MMKV源起

在微信客户端的日常运营中,时不时就会爆发特殊文字引起的系统crash,iOS微信特殊字符保护方案,文章里面设计的技术方案是在关键代码前后进行计数器的加减,通过检查计数器的异常,来发现引起闪退的异常文字。在会话列表、会话界面等有大量cell的地方,希望新加的计数器不会影响滑动性能;另外这些计数器还要永久存储下来---因为闪退随时有可能发生。这就需要一个性能非常高的通用key-value存储组件,考察了SharedPreferences、NSUserDefaults、SQLite等常见组件,发现都没能满足如此苛刻的性能要求。考虑到这个防crash方案最主要的诉求还是实时写入,而mmap内存映射文件刚好满足这种需求,所以尝试通过它实现一套key-value组件。

MMKV原理

内存准备:通过mmap内存映射文件,提供一段可以随时写入的内存块,APP只管往里面写数据,有操作系统负责将内存回写到文件,不用担心crash导致数据丢失。

数据组织:数据序列化方面选用protobuf协议,pb在性能和空间占用上都有不错的表现。

写入优化:考虑到主要使用场景是频繁的写入更新,所以需要有增量更新的能力。因此考虑将增量kv对象序列化后,append到内存末尾。

空间增长:使用append实现增量更新带来了一个新的问题,就是不断append的话,文件大小会增长的不可控。需要在性能和空间上做一个折中。

更详细的设计原理可以参考design · Tencent/MMKV Wiki · GitHub文档。

特点:

存储方式:MMKV基于内存映射文件(Memory-Mapped File)实现,数据存储在磁盘上,但是可以直接从内存访问,避免了频繁的磁盘I/O操作。

性能:相比于SharedPreferences,MMKV提供了更高的性能,尤其是在大量数据存储和访问的场景中表现更好。

加密支持:支持加密,可以加密存储的数据,适用于需要加密保护的场景。

线程安全:MMKV是线程安全的,多线程可以同时访问。

易用性:API使用方式与SharedPreferences类似,迁移成本较低。

优点:

支持多进程

如果需要多进程通信,那暂时就只能用MMKV了。

"快"

单进程性能

MMKV &  SharedPreferences & SQLite读写速度对比:

MMKV 在写入性能上远远超越 SharedPreferences & SQLite,在读取性能上也有相近或超越的表现。

多线程性能

MMKV &  SharedPreferences & SQLite读写速度对比:

可见,MMKV无论是在写入性能还是在读取性能都要远远超越SharedPreferences 和 SQLite,MMKV在Android多进程key-value存储组件上是不二之选。

缺点:

写入大数据速度较慢

当使用MMKV写入大的字符串数据时,相比于SP和DataStore会慢一些,但是开发中基本不会写入那么大的字符串。

可能会丢数据

当设备突然断电关机等意外现象时,刚好数据保存在一半的情况下,此时文件就会发生损坏。这种问题是不可避免的,MMKV的底层机制在断电关机之类的操作系统级别的崩溃,没有做备份还原操作,数据就会损失重置;MMKV底层的原理是内存映射,它不是实时的将内存中的数据写入到磁盘中,会有一定的滞后性,MMKV定位于高频写入可能这就是它不实时写入磁盘的原因吧。

而SharedPreferences和DataStore的应对方式是在每次写入新数据之前都对现有文件做一次自动备份,这样在发生意外出现文件损坏之后,它们机会把备份的数据恢复过来。

 DataStore

Google提供的现代化数据存储解决方案。分为Preferences DataStore 和 Proto DataStore两类,前者也是基于键值对的存储,后者基于ProtoBuf。用Kotlin协程和Flow实现异步、响应式编程;类型安全、无业务侵入,支持直接保存对象。

依赖配置

implementation "androidx.datastore:datastore-preferences:1.0.0"

Preferences DataStore

import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapprivate val Context.dataStore by preferencesDataStore("settings")object PreferencesKeys {val EXAMPLE_KEY = stringPreferencesKey("example_key")
}suspend fun saveData(context: Context, value: String) {context.dataStore.edit { preferences ->preferences[PreferencesKeys.EXAMPLE_KEY] = value}
}fun getData(context: Context): Flow<String?> {return context.dataStore.data.map { preferences ->preferences[PreferencesKeys.EXAMPLE_KEY]}
}
// 定义一个 Preferences
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")// 存储数据
val usernameKey = stringPreferencesKey("username")
val ageKey = intPreferencesKey("age")suspend fun saveData(context: Context) {context.dataStore.edit { preferences ->preferences[usernameKey] = "John"preferences[ageKey] = 30}
}// 获取数据
suspend fun readData(context: Context): String? {val preferences = context.dataStore.data.first()return preferences[usernameKey]
}

Proto DataStore

// Proto 文件
message UserSettings {string username = 1;int32 age = 2;
}// Proto DataStore 用法
val userSettingsDataStore: DataStore<UserSettings> = context.createDataStore(fileName = "user_settings.pb",serializer = UserSettingsSerializer
)// 存储数据
suspend fun saveProtoData() {userSettingsDataStore.updateData { currentSettings ->currentSettings.toBuilder().setUsername("John").setAge(30).build()}
}// 获取数据
suspend fun readProtoData(): UserSettings? {return userSettingsDataStore.data.first()
}

特点:

异步操作:DataStore 是完全基于 Kotlin 协程的,操作是异步的,避免了阻塞主线程的问题。

类型安全:支持使用类型安全的 Proto 数据格式来存储结构化的数据,避免了在 SharedPreferences 中手动转换数据类型的麻烦。

迁移支持:从 SharedPreferences 迁移到 DataStore 方便且有帮助,尤其是对于大部分简单的键值对存储需求。

优点:

性能高、不卡顿、不丢数据

DataStroe基于Kotlin协程实现和使用,官方主推性能,主线程读写(不管大小)数据都不卡顿(MMKV读写长字符串时可能会发生卡顿)。

官方站台主推数据存储方案

官方代替SharedPreferences方案,SP有的基础上并优化了性能问题,选择存储方案时应该优先考虑。

缺点:

不支持多进程

暂时不支持多进程。

需要支持KT协程

DataStroe基于Kotlin协程实现和使用,如果你的项目还是纯Java的话,还是用SP忍一忍吧。

总结对比:

特性SharedPreferencesMMKVJetpack DataStore
存储方式键值对,XML 文件键值对,内存映射文件键值对(Preferences),协议缓冲(Proto)
性能较差,尤其是在数据量较大时高效,支持大规模数据高效,支持异步操作
线程安全需要手动同步操作默认线程安全默认线程安全
加密支持不支持支持不支持(需要额外处理)
支持结构化数据存储不支持不支持支持(Proto DataStore)
API 设计简单,传统简单,基于 SharedPreferences 类似更现代,基于 Kotlin 协程和流
使用场景存储简单设置和小型数据高性能存储,尤其适用于需要处理大量数据的场景适用于需要异步操作的场景,支持结构化数据

结论:

  • SharedPreferences:适合存储小型、简单的配置信息,操作简单,适用于老旧项目。
  • MMKV:适用于对性能有较高要求的应用,特别是数据量较大时,性能优越,且支持加密。
  • Jetpack DataStore:推荐用于现代 Android 应用,尤其是需要异步处理或结构化数据存储的场景,支持 Kotlin 协程和流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66404.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mycat2使用教程

运行环境框架包 下载地址&#xff1a;mycat2-install-template-1.20.zip 运行包 下载地址&#xff1a;mycat2-1.21-release-jar-with-dependencies.jar 说明 解压运行环境框架包&#xff0c;将运行包放到mycat的lib目录下 配置数据源 打开conf/datasources/prototypeDs.da…

望获实时Linux系统与大语言模型深度融合,开创实时智能无限可能!

大语言模型的崛起为智能化应用开辟了新的可能性。借助深度学习技术&#xff0c;这些模型能够理解和生成自然语言&#xff0c;处理复杂的文本和语义信息。这使得它们在诸如人机问答、内容生成和数据分析等领域展现出巨大的潜力。在实时控制领域&#xff0c;大语言模型能够显著提…

基于 GEE Sentinel-1 数据集提取水体

目录 1 水体提取原理 2 完整代码 3 运行结果 1 水体提取原理 水体提取是地理信息和遥感技术的关键应用之一&#xff0c;对于多个领域都具有重要的应用价值。它有助于更好地管理水资源&#xff0c;保护环境&#xff0c;减少灾害风险&#xff0c;促进可持续发展&#xff0c;以…

『SQLite』表的创建、修改和删除

本节摘要&#xff1a;主要讲述SQLite中创建、删除、修改表等操作。 创建表 CREATE TABLE 语句来创建表。 修改表 ALTER TABLE 语句来修改表名称、已有表字段&#xff0c;或者新增字段。 删除表 DROP TABLE 语句用来删除表. 注意&#xff1a; 上述内容详细讲解见文章&#…

java自动更新实体类的创建时间和更新时间3种方式

一、使用数据库的自动更新机制 1.创建表时设置创建时间和更新时间的默认值 ALTER TABLE lspace_diga.hot_city MODIFY COLUMN gmtCreate TIMESTAMP DEFAULT CURRENT_TIMESTAMP;ALTER TABLE lspace_diga.hot_city MODIFY COLUMN gmtModified TIMESTAMP DEFAULT CURRENT_TIMEST…

深入理解 pytest_runtest_makereport:如何在 pytest 中自定义测试报告

pytest_runtest_makereport 是 pytest 系统中的一个钩子函数&#xff0c;它允许我们在测试执行时获取测试的报告信息。通过这个钩子&#xff0c;我们可以在测试运行时&#xff08;无论是成功、失败还是跳过&#xff09;对测试结果进一步处理&#xff0c;比如记录日志、添加自定…

w~自动驾驶~合集16

我自己的原文哦~ https://blog.51cto.com/whaosoft/12765612 #SIMPL 用于自动驾驶的简单高效的多智能体运动预测基准 原标题&#xff1a;SIMPL: A Simple and Efficient Multi-agent Motion Prediction Baseline for Autonomous Driving 论文链接&#xff1a;https://ar…

外驱功率管电流型PWM控制芯片CRE6281B1

CRE6281B1 是一款外驱功率管的高度集成的电流型PWM 控制 IC&#xff0c;为高性能、低待机功率、低成本、高效率的隔离型反激式开关电源控制器。在满载时&#xff0c;CRE6281B1工作在固定频率(65kHz)模式。在负载较低时&#xff0c;CRE6281B1采用节能模式&#xff0c;实现较高的…

2025年安卓面试复习总结

文章目录 深入理解并熟练运用常用设计模式及反射原理&#xff0c;能够自定义注解及泛型&#xff0c;多次通过设计模式对 app 代码进行高效重构&#xff0c;显著提升代码的可维护性与扩展性。设计模式自定义注解泛型Kotlin泛型 精通多线程原理&#xff0c;对 ThreadPoolExecutor…

【微服务】SpringBoot 整合Redis实现延时任务处理使用详解

目录 一、前言 二、延迟任务的高频使用场景 三、延迟任务常用解决方案 3.1 Quartz 3.2 DelayQueue 3.2.1 Timer + TimerTask 3.2.2 ScheduledExecutorService 3.3 Redis sorted set 3.4 RabbitMQ 四、Redis实现延时队列操作实战 4.1 Redis Sorted Set 概述 4.1.1 Re…

加固服务器有什么用?

为什么越来越多的企业和个人都在加固他们的服务器&#xff1f;加固服务器不仅可以保护数据安全&#xff0c;还能提升整体系统的稳定性和可靠性。下面是聚名网的一些介绍。 加固服务器的首要目的就是提高安全性。随着网络攻击手段的不断演变&#xff0c;黑客和恶意软件的威胁也…

web移动端UI框架

文章目录 Vant简介主要特点和功能适用场景和用户评价 Mint UI简介主要特点和功能 cube-ui简介特性 iView Weapp简介 uni-app简介 Vant 使用vue3版本官网&#xff1a;https://vant-ui.github.io/vant/#/zh-CN/ 适用vue2版本官网&#xff1a;https://vant-ui.github.io/vant/v2/…

亚信安全2025年第1期《勒索家族和勒索事件监控报告》

本周态势快速感知 本周全球共监测到勒索事件51起&#xff0c;本周勒索事件数量降至近一年来最低&#xff0c;需注意防范。从整体上看Ransomhub依旧是影响最严重的勒索家族&#xff1b;本周Acrusmedia和Safepay也是两个活动频繁的恶意家族&#xff0c;需要注意防范。本周&#…

git①111

如果你只想提交并推送一个文件&#xff0c;而不是所有修改过的文件&#xff0c;可以按照以下步骤进行操作&#xff1a; 步骤&#xff1a; 1.查看当前修改的文件&#xff1a; 使用 git status 来查看哪些文件被修改了。 git status 你会看到类似以下的输出&#xff0c;列出了…

linux网络 | http结尾、理解长连接短链接与cookie

前言&#xff1a;本节是http章节的最后一部分&#xff0c;主要解释一些小概念。讲解到了HTTP的方法&#xff0c;表单&#xff0c; 重定向等等。 现在废话不多说&#xff0c; 开始我们的学习吧。 ps&#xff1a;本节内容都是概念&#xff0c; 知道就行&#xff0c; 友友们放心观…

韩国机场WebGIS可视化集合Google遥感影像分析

目录 前言 一、相关基础数据介绍 1、韩国的机场信息 2、空间数据准备 二、Leaflet叠加Google地图 1、叠加google地图 2、空间点的标记及展示 3、韩国机场空间分布 三、相关成果展示 1、务安国际机场 2、有同类问题的机场 四、总结 前言 12月29日8时57分左右务安国际机…

基于Django的农业管理系统

一、系统背景与意义 随着信息技术的不断发展&#xff0c;农业生产也逐渐向智能化、信息化转型。基于Django的农业管理系统正是为了满足这一需求而设计的。该系统通过集成先进的信息技术&#xff0c;旨在提升农业生产效率&#xff0c;优化资源配置&#xff0c;减少人工干预&…

后台管理系统引导功能的实现

引导是软件中经常见到的一个功能&#xff0c;无论是在后台项目还是前台或者是移动端项目中。 那么对于引导页而言&#xff0c;它是如何实现的呢&#xff1f;通常情况下引导页是通过 聚焦 的方式&#xff0c;高亮一块视图&#xff0c;然后通过文字解释的形式来告知用户该功能的作…

nuxt3访问localstorage,sessionStorage

nuxt3在客户端的时候可以正常使用localstorage,sessionStorage&#xff0c;但是如果在服务端&#xff0c;是没有这些方法的&#xff0c;这个时候可以把数据存到cookie里&#xff0c;通过cookie来维护数据 // 访问cookie const token getCookie(token) console.log(token)// 更…

Yolov8训练方式以及C#中读取yolov8+onnx模型进行目标检测.NET 6.0

目录 首先你要定义v8的模型特征文件 下方是完整的模型编写函数 然后你要在控件窗体中定义应用这些方法以及函数 一、定义你的标签 二、下方是定义模块和坐标的方法 三、画框 完整推理函数代码块 效果展示 完整源码 训练其实和yolov10差不多 因为v10就是在v8的基础上做了优…