当前位置: 首页 > news >正文

Android 持久化存储原理与使用解析

一、核心存储方案详解
1. SharedPreferences (SP)

使用方式:

// 获取实例
SharedPreferences sp = getSharedPreferences("user_prefs", MODE_PRIVATE);// 写入数据
sp.edit().putString("username", "john_doe").putInt("login_count", 5).apply(); // 异步提交// 读取数据
String username = sp.getString("username", "default");
int loginCount = sp.getInt("login_count", 0);

原理流程:

优点:

  • 简单易用,Android 原生支持

  • 适合存储小量键值对数据

缺点:

  • ⚠️ 全量写入:修改单个值也重写整个文件

  • ⚠️ ANR 风险apply() 异步提交在生命周期回调时可能阻塞主线程

  • ❌ 多进程不安全MODE_MULTI_PROCESS 已废弃

  • ❌ 无类型安全:读取时需手动转换类型

使用场景:
低频修改的简单配置(如用户主题设置、功能开关)


2. MMKV(微信开源)

使用方式:

// build.gradle
implementation 'com.tencent:mmkv:1.3.0'
// 初始化
String rootDir = MMKV.initialize(this);// 获取实例
MMKV kv = MMKV.defaultMMKV();// 写入数据
kv.encode("session_token", "a1b2c3d4");
kv.encode("user_points", 1500);// 读取数据
String token = kv.decodeString("session_token");
int points = kv.decodeInt("user_points");

原理流程:

优点:

  • ⚡ 高性能:读写速度比SP快100倍+

  • 🔒 多进程支持:完善的文件锁机制

  • 📦 高效存储:Protobuf编码节省50%空间

  • 🔐 加密支持:AES加密敏感数据

缺点:

  • ➕ 需引入三方库

  • ⚠️ 数据模型较简单(适合键值对)

使用场景:
高频读写数据(如用户积分)、多进程共享配置、替代SP的所有场景


3. DataStore(Google官方)

使用方式(Preferences DataStore):

// build.gradle
implementation "androidx.datastore:datastore-preferences:1.0.0"
// 定义Key
val USER_NAME = stringPreferencesKey("user_name")
val LOGIN_COUNT = intPreferencesKey("login_count")// 创建DataStore
val dataStore: DataStore<Preferences> = context.createDataStore(name = "settings")// 写入数据
suspend fun saveData(name: String, count: Int) {dataStore.edit { preferences ->preferences[USER_NAME] = namepreferences[LOGIN_COUNT] = count}
}// 读取数据
val userNameFlow: Flow<String> = dataStore.data.map { preferences -> preferences[USER_NAME] ?: "" }

Proto DataStore(类型安全):

// user_prefs.proto
message UserPrefs {string name = 1;int32 login_count = 2;bool is_premium = 3;
}
val Context.userPrefsStore: DataStore<UserPrefs> by dataStore(fileName = "user_prefs.pb",serializer = UserPrefsSerializer
)// 直接操作对象
viewModelScope.launch {context.userPrefsStore.updateData { prefs ->prefs.toBuilder().setLoginCount(10).build()}
}

优点:

  • 🛡️ 类型安全:Protobuf 编译时校验

  • ⚡ 异步操作:基于协程,无主线程阻塞风险

  • 🔄 数据流支持:响应式数据更新

  • 🔄 平滑迁移:提供SP迁移工具

缺点:

  • 📚 学习曲线较陡峭(需掌握协程/Protobuf)

  • 🚫 不支持多进程

使用场景:
新项目开发、复杂数据模型存储、响应式配置更新


二、Intent 数据传输限制

使用方式:

// 传递数据
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("user_id", 12345);
intent.putExtra("document", pdfByteArray); // 危险操作!
startActivity(intent);// 接收数据
int userId = getIntent().getIntExtra("user_id", 0);
byte[] pdfData = getIntent().getByteArrayExtra("document");

限制原因:

为什么只能传少量数据?

  1. Binder 限制:IPC传输缓冲区固定为 1MB(Android 8.0+ 部分设备2MB)

  2. 性能问题:大数据序列化/反序列化消耗CPU和内存

  3. 稳定性风险:可能引发OOM或ANR

  4. 生命周期不匹配:Activity可能被销毁重建,丢失数据

解决方案:

数据大小推荐方案示例
< 100KB直接Intent传递intent.putExtra("id", 123)
100KB ~ 1MBFileProvider共享文件传递content:// URI
> 1MB持久化存储+标识传递数据库ID/文件路径
复杂对象Parcelable序列化实现Parcelable接口

三、方案对比与选型指南

维度SharedPreferencesMMKVDataStoreIntent
存储类型键值对键值对键值对/Protobuf对象临时数据
性能极高
线程安全主线程风险安全安全(协程)主线程安全
多进程✅(IPC)
ANR风险高(大数据)
数据大小<1MB无限制无限制<1MB
推荐场景低频配置项高频读写/多进程新项目/类型安全小数据传递

四、常见问题总结

Q1:SharedPreferences有什么缺陷?如何优化?

A:
主要缺陷:

  1. 全量写入导致I/O性能差

  2. apply()异步提交可能引发ANR(ActivityThread等待QueuedWork)

  3. 多进程不安全(MODE_MULTI_PROCESS已废弃)

  4. 无类型安全检查

优化方案:

  1. 迁移到MMKV或DataStore

  2. 避免存储超过1MB数据

  3. 对高频修改项单独拆分文件

Q2:MMKV为什么比SharedPreferences快?

A:
MMKV通过三重优化实现高性能:

  1. mmap内存映射:文件直连内存,省去系统调用和数据拷贝

  2. Protobuf编码:比XML节省50%+存储空间

  3. 增量更新:只追加修改数据,避免全文件重写

  4. 多进程锁:通过文件锁实现安全并发访问

Q3:DataStore相比SP的核心优势?

A:
DataStore的四大优势:

  1. 无ANR设计:协程异步API彻底避免主线程阻塞

  2. 类型安全:Proto DataStore支持编译时类型检查

  3. 响应式编程:通过Flow实现数据变更监听

  4. 事务支持edit{}块内操作保证原子性

Q4:Intent为什么不能传大数据?

A:
Intent传输受限于三点:

  1. Binder IPC限制:传输缓冲区固定1-2MB

  2. 序列化开销:大数据序列化消耗CPU/内存,可能导致ANR

  3. 生命周期风险:Activity重建时系统可能丢弃Intent数据

解决方案:

  • <1MB:直接使用Intent

  • 1-10MB:通过FileProvider传递URI

  • 10MB:持久化存储后传递标识符


五、场景选择


六、高频问题总结

  1. SP的apply()commit()区别?

    apply()异步提交但不返回结果,commit()同步提交并返回boolean结果。注意apply()可能导致ANR。

  2. MMKV如何保证多进程安全?

    通过fcntl文件锁实现写互斥,跨进程场景使用pthread_mutex(需处理robust属性)。

  3. DataStore如何从SP迁移?

    val dataStore = createDataStore(preferencesMigration = SharedPreferencesMigration(context, "sp_name"))

  4. Intent传递大Bitmap的正确方式?

    // 步骤1:保存到文件
    File file = saveBitmapToCache(bitmap); // 步骤2:通过FileProvider生成URI
    Uri uri = FileProvider.getUriForFile(context, "com.example.provider", file);// 步骤3:传递URI并设置权限
    intent.putExtra("image_uri", uri);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
  5. 为什么推荐用FileProvider不用绝对路径?

    FileProvider提供临时权限控制,避免直接暴露文件路径的安全风险

http://www.dtcms.com/a/294307.html

相关文章:

  • MATLAB | 绘图复刻(二十二)| 带树状图的三角热图合集
  • 个性化网页计数器
  • C 语言介绍
  • 【数据结构】二叉树的链式结构--用C语言实现
  • 嵌入式linux下的NES游戏显示效果优化方案:infoNES显示效果优化
  • 我用EV-21569-SOM评估来开发ADSP-21569(十三)-SigmaStudio Plus做开发(4)
  • Web前端开发:JavaScript遍历方法详解与对比
  • 安全防护-FCW
  • [HarmonyOS] HarmonyOS LiteOS-A 设备开发全流程指南
  • Linux第三天Linux基础命令(二)
  • 服务器对kaggle比赛的数据集下载
  • SAP-ABAP:SELECT语句验证字段和验证方法详解
  • OSPF路由协议——上
  • 28. 找出字符串中第一个匹配项的下标
  • vue3中el-table表头筛选
  • Flink 状态管理设计详解:StateBackend、State、RocksDB和Namespace
  • 谷粒商城篇章13--P340-P360--k8s/KubeSphere【高可用集群篇一】
  • 抖音集团基于Flink的亿级RPS实时计算优化实践
  • k8s pvc是否可绑定在多个pod上
  • 飞算JavaAI:从“工具革命”到“认知革命”——开发者如何借力AI重构技术竞争力
  • SpringBoot 内嵌 Tomcat 的相关配置
  • MySQL binlog解析
  • linux c语言进阶 - 线程,通信方式,安全方式(多并发)
  • Linux中常见的中英文单词对照表
  • 低代码中的统计模型是什么?有什么作用?
  • 第一二章知识点
  • 交换机的六种常见连接方式配置(基于华为eNSP)
  • 洛谷刷题7.23
  • 电子公章怎么弄到合同上?2025最新指南
  • Android NDK与JNI深度解析