> 技术文档 > Android 持久化存储原理与使用解析

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.gradleimplementation \'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.gradleimplementation \"androidx.datastore:datastore-preferences:1.0.0\"
// 定义Keyval USER_NAME = stringPreferencesKey(\"user_name\")val LOGIN_COUNT = intPreferencesKey(\"login_count\")// 创建DataStoreval dataStore: DataStore = context.createDataStore(name = \"settings\")// 写入数据suspend fun saveData(name: String, count: Int) { dataStore.edit { preferences -> preferences[USER_NAME] = name preferences[LOGIN_COUNT] = count }}// 读取数据val userNameFlow: Flow = dataStore.data .map { preferences -> preferences[USER_NAME] ?: \"\" }

Proto DataStore(类型安全):

// user_prefs.protomessage UserPrefs { string name = 1; int32 login_count = 2; bool is_premium = 3;}
val Context.userPrefsStore: DataStore 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 ~ 1MB FileProvider共享文件 传递content:// URI > 1MB 持久化存储+标识传递 数据库ID/文件路径 复杂对象 Parcelable序列化 实现Parcelable接口

三、方案对比与选型指南

维度 SharedPreferences MMKV DataStore Intent 存储类型 键值对 键值对 键值对/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生成URIUri 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提供临时权限控制,避免直接暴露文件路径的安全风险