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\");
限制原因:
为什么只能传少量数据?
-
Binder 限制:IPC传输缓冲区固定为 1MB(Android 8.0+ 部分设备2MB)
-
性能问题:大数据序列化/反序列化消耗CPU和内存
-
稳定性风险:可能引发OOM或ANR
-
生命周期不匹配:Activity可能被销毁重建,丢失数据
解决方案:
intent.putExtra(\"id\", 123)
三、方案对比与选型指南
四、常见问题总结
Q1:SharedPreferences有什么缺陷?如何优化?
A:
主要缺陷:
全量写入导致I/O性能差
apply()
异步提交可能引发ANR(ActivityThread等待QueuedWork)多进程不安全(
MODE_MULTI_PROCESS
已废弃)无类型安全检查
优化方案:
迁移到MMKV或DataStore
避免存储超过1MB数据
对高频修改项单独拆分文件
Q2:MMKV为什么比SharedPreferences快?
A:
MMKV通过三重优化实现高性能:
mmap内存映射:文件直连内存,省去系统调用和数据拷贝
Protobuf编码:比XML节省50%+存储空间
增量更新:只追加修改数据,避免全文件重写
多进程锁:通过文件锁实现安全并发访问
Q3:DataStore相比SP的核心优势?
A:
DataStore的四大优势:
无ANR设计:协程异步API彻底避免主线程阻塞
类型安全:Proto DataStore支持编译时类型检查
响应式编程:通过Flow实现数据变更监听
事务支持:
edit{}
块内操作保证原子性
Q4:Intent为什么不能传大数据?
A:
Intent传输受限于三点:
Binder IPC限制:传输缓冲区固定1-2MB
序列化开销:大数据序列化消耗CPU/内存,可能导致ANR
生命周期风险:Activity重建时系统可能丢弃Intent数据
解决方案:
<1MB:直接使用Intent
1-10MB:通过FileProvider传递URI
10MB:持久化存储后传递标识符
五、场景选择
六、高频问题总结
-
SP的
apply()
和commit()
区别?apply()
异步提交但不返回结果,commit()
同步提交并返回boolean结果。注意apply()
可能导致ANR。 -
MMKV如何保证多进程安全?
通过
fcntl
文件锁实现写互斥,跨进程场景使用pthread_mutex
(需处理robust属性)。 -
DataStore如何从SP迁移?
val dataStore = createDataStore(preferencesMigration = SharedPreferencesMigration(context, \"sp_name\"))
-
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);
-
为什么推荐用FileProvider不用绝对路径?
FileProvider提供临时权限控制,避免直接暴露文件路径的安全风险