肝货!Android 持久化技术全解:SharedPreference + 文件存储实战一本通
肝货!Android 持久化技术全解:SharedPreference + 文件存储实战一本通
目录
- 什么是持久化?
- Android 持久化方式一览
- SharedPreference 详解
- 文件存储详解
- 内部存储 vs 外部存储
- 实战代码演示
- 注意事项与最佳实践
1. 什么是持久化?
概念
持久化是将数据存储到非易失性存储(断电后数据不丢失)中。
内存(易失性):程序运行时使用,断电丢失 ↓ 持久化 磁盘(非易失性):数据永久保存手机关机再开机后,数据还在 → 持久化成功
2. Android 持久化方式一览
| 方式 | 适用场景 | 数据量 | 速度 |
|---|---|---|---|
| SharedPreference | 键值对、设置项 | 小 | 快 |
| 文件存储 | 文本、大数据 | 大 | 较慢 |
| SQLite 数据库 | 结构化数据 | 大量 | 中 |
| Room | 复杂查询 | 大量 | 快(封装后) |
| ContentProvider | 数据共享 | 不限 | 中 |
| DataStore | 替代 SharedPreference | 小-中 | 快 |
3. SharedPreference 详解
3.1 什么是 SharedPreference?
用于存储键值对数据的轻量级存储方式,适合保存配置、状态等小数据。
3.2 基本使用
// 获取 SharedPreference 对象valprefs=getSharedPreferences("my_prefs",Context.MODE_PRIVATE)// 保存数据prefs.edit().apply{putString("username","张三")putInt("age",25)putBoolean("isLogin",true)apply()// 异步保存// 或 commit() 同步保存,返回 boolean}// 读取数据valusername=prefs.getString("username","默认值")valage=prefs.getInt("age",0)valisLogin=prefs.getBoolean("isLogin",false)3.3 两种获取方式
// 方式1:指定名称(一个应用可以有多个 Prefs 文件)valprefs1=getSharedPreferences("user_prefs",Context.MODE_PRIVATE)valprefs2=getSharedPreferences("app_settings",Context.MODE_PRIVATE)// 方式2:Activity 私有(自动使用 Activity 名称作为文件名)valprefs=getPreferences(Context.MODE_PRIVATE)3.4 MODE modes(存储模式)
| 模式 | 说明 |
|---|---|
MODE_PRIVATE | 只有本应用可访问(最常用) |
MODE_WORLD_READABLE | 其他应用可读(已废弃) |
MODE_WORLD_WRITABLE | 其他应用可写(已废弃) |
MODE_MULTI_PROCESS | 多进程访问(不推荐) |
3.5 apply vs commit
| 对比 | apply() | commit() |
|---|---|---|
| 返回值 | void | boolean(是否成功) |
| 执行方式 | 异步 | 同步 |
| 性能 | 更快 | 稍慢 |
| 安全性 | 丢失的机会更小 | 写入更安全 |
推荐:大多数情况用apply(),如果需要知道写入结果再用commit()。
4. 文件存储详解
4.1 内部存储
文件存储在应用私有目录下,其他应用无法访问。
// 保存到内部存储valcontent="Hello World"valfileName="test.txt"valfos=openFileOutput(fileName,Context.MODE_PRIVATE)fos.write(content.toByteArray())fos.close()// 读取内部存储valfis=openFileInput(fileName)valbytes=ByteArray(fis.available())fis.read(bytes)fis.close()valcontent=String(bytes)4.2 内部存储目录
// 获取各种目录filesDir// /data/data/<package>/filescacheDir// /data/data/<package>/cachefilesDir.path// 查看路径// 文件操作valfile=File(filesDir,"test.txt")file.writeText("Hello")valcontent=file.readText()4.3 外部存储
需要权限,且可能不可用(SD 卡拔出、存储满)。
// 外部存储路径valexternalFilesDir=getExternalFilesDir(null)// /storage/emulated/0/Android/data/<package>/files// 检查外部存储是否可用funisExternalStorageWritable():Boolean{returnEnvironment.getExternalStorageState()==Environment.MEDIA_MOUNTED}4.4 权限说明
<!-- Android 10+ 外部存储需要权限(部分场景)--><uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE"/><!-- Android 13+ 使用 MediaStore API 访问媒体文件 -->5. 内部存储 vs 外部存储
| 对比 | 内部存储 | 外部存储 |
|---|---|---|
| 路径 | /data/data/<package>/files/ | /storage/.../Android/data/<package>/files/ |
| 访问权限 | 本应用私有,其他应用不可访问 | 可被其他应用读取 |
| 卸载应用 | 数据被删除 | Android 10+ 部分被删除 |
| 容量 | 有限,但有保证 | 受限于设备存储空间 |
| 速度 | 较快 | 较慢 |
| 需要权限 | 不需要 | 需要(某些场景) |
6. 实战代码演示
6.1 SharedPreference 完整示例
classPersistenceActivity:AppCompatActivity(){companionobject{constvalPREFS_NAME="my_prefs"constvalKEY_USERNAME="username"constvalKEY_PASSWORD="password"}privatelateinitvaretUsername:EditTextprivatelateinitvaretPassword:EditTextoverridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContentView(R.layout.activity_persistence)// 保存数据funsaveToSharedPreference(){valusername=etUsername.text.toString()valpassword=etPassword.text.toString()valprefs=getSharedPreferences(PREFS_NAME,Context.MODE_PRIVATE)prefs.edit().apply{putString(KEY_USERNAME,username)putString(KEY_PASSWORD,password)apply()}}// 读取数据funloadFromSharedPreference(){valprefs=getSharedPreferences(PREFS_NAME,Context.MODE_PRIVATE)valusername=prefs.getString(KEY_USERNAME,"未设置")valpassword=prefs.getString(KEY_PASSWORD,"未设置")tvResult.text="用户名:$username\n密码:$password"}}}6.2 文件存储完整示例
companionobject{constvalFILE_NAME="user_data.txt"}// 保存到文件privatefunsaveToFile(username:String,password:String){valcontent="用户名:$username\n密码:$password"try{valfos=openFileOutput(FILE_NAME,Context.MODE_PRIVATE)fos.write(content.toByteArray())fos.close()Toast.makeText(this,"文件保存成功",Toast.LENGTH_SHORT).show()}catch(e:Exception){e.printStackTrace()Toast.makeText(this,"保存失败:${e.message}",Toast.LENGTH_SHORT).show()}}// 从文件读取privatefunloadFromFile():String{returntry{valfile=File(filesDir,FILE_NAME)if(!file.exists())return"文件不存在"file.readText()}catch(e:Exception){"读取失败:${e.message}"}}// 删除文件privatefundeleteFile(){valfile=File(filesDir,FILE_NAME)if(file.exists()){file.delete()}}6.3 工具类封装(推荐)
objectPrefsManager{privateconstvalNAME="app_prefs"funsaveUser(context:Context,username:String,token:String){context.getSharedPreferences(NAME,Context.MODE_PRIVATE).edit().apply{putString("username",username)putString("token",token)apply()}}fungetUser(context:Context):Pair<String,String>{valprefs=context.getSharedPreferences(NAME,Context.MODE_PRIVATE)returnPair(prefs.getString("username","")?:"",prefs.getString("token","")?:"")}funclearUser(context:Context){context.getSharedPreferences(NAME,Context.MODE_PRIVATE).edit().clear().apply()}funisLoggedIn(context:Context):Boolean{valprefs=context.getSharedPreferences(NAME,Context.MODE_PRIVATE)returnprefs.getString("token","").isNotEmpty()}}7. 注意事项与最佳实践
7.1 SharedPreference 注意事项
⚠️ 不要存储大量数据( > 1MB) ⚠️ 不要存储敏感信息(Token 等),加密后存储 ⚠️ MODE_MULTI_PROCESS 不推荐,线程不安全 ⚠️ 频繁修改同一个 key 性能差优化建议:
// ❌ 错误:频繁修改repeat(100){prefs.edit().putInt("count",it).apply()}// ✅ 正确:批量修改prefs.edit().apply{repeat(100){putInt("count_$it",it)}}7.2 文件存储注意事项
⚠️ 外部存储需要权限 ⚠️ 存储前检查空间是否足够 ⚠️ 大文件使用缓冲流 ⚠️ 操作在后台线程执行7.3 数据安全建议
// 如果必须存储敏感信息,使用 EncryptedSharedPreferencevalmasterKey=MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()valprefs=EncryptedSharedPreferences.create(context,"secure_prefs",masterKey,EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)7.4 选择存储方式
| 场景 | 推荐方式 |
|---|---|
| 用户设置、登录状态 | SharedPreference |
| 简单文本数据 | 文件存储 |
| 结构化数据 | Room/SQLite |
| 大量数据 | Room |
| 跨应用共享 | ContentProvider |
| 替代 SharedPreference | DataStore |
总结
- 持久化是将数据保存到磁盘,断电后不丢失
- SharedPreference适合存储键值对(设置、登录状态)
- 文件存储适合存储大量文本或二进制数据
- 内部存储本应用私有,不需要权限
- 外部存储需要权限,可被其他应用访问
- 安全:敏感信息用 EncryptedSharedPreference 或加密
