1. 概述 (Overview)
在 开发中,持久化存储是必不可少的功能。Android 提供了多种数据存储方式,其中 应用专属文件 和 应用偏好设置(SharedPreferences) 是两种最常用、最基础的轻量级存储方案。
- 应用专属文件 (App-Specific Files):用于存储应用私有的、结构化的或非结构化的文件数据(如文本、图片、JSON 等)。这些文件只能被创建它们的应用访问,当应用被卸载时,文件也会被自动删除。
- 应用偏好设置 (SharedPreferences):用于存储简单的键值对(Key-Value)数据,通常用于保存用户的配置选项、登录状态、开关设置等。它以 XML 文件的形式存储在应用的私有目录中。
这两种方式都遵循"沙盒"安全模型,确保了应用数据的私密性和安全性。
2. 应用专属文件 (App-Specific Files)
Android 将应用的文件存储分为两类:内部存储(Internal Storage) 和 外部存储(External Storage) 。对于大多数场景,尤其是需要保证隐私的数据,应优先使用内部存储。
2.1 内部存储 (Internal Storage)
内部存储位于 /data/data/<package_name>/files/ 目录下,是应用私有的,其他应用无法直接访问。
2.1.1 获取内部存储路径
java
// Java - 在 Activity 或 Context 中
File file = new File(getFilesDir(), "example.txt");
// 或者直接获取输出流
FileOutputStream fos = openFileOutput("example.txt", Context.MODE_PRIVATE);
getFilesDir(): 返回一个File对象,指向应用的内部文件目录。openFileOutput(String name, int mode): 直接打开一个文件的输出流进行写入。
2.1.2 写入文件
java
// Java
try {
FileOutputStream fos = openFileOutput("config.json", Context.MODE_PRIVATE);
fos.write("{\"theme\":\"dark\"}".getBytes());
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
2.1.3 读取文件
java
// Java
try {
FileInputStream fis = openFileInput("config.json");
StringBuilder content = new StringBuilder();
int ch;
while ((ch = fis.read()) != -1) {
content.append((char) ch);
}
fis.close();
String jsonContent = content.toString();
} catch (IOException e) {
e.printStackTrace();
}
注意 :
Context.MODE_PRIVATE是唯一可用的模式,确保文件是私有的。
2.2 外部存储 (External Storage)
外部存储(如 SD 卡)可供所有应用和用户访问。从 Android 10 (API level 29) 开始,引入了分区存储(Scoped Storage) ,限制了应用对外部存储的访问范围。对于应用专属的外部文件,应使用 getExternalFilesDir()。
java
// Java - 获取应用专属的外部文件目录
File externalFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "report.pdf");
// 这个文件位于 /storage/emulated/0/Android/data/<package_name>/files/Documents/
优点: 即使应用被卸载,这些文件也不会被自动删除(除非用户手动清理),且不会占用内部存储空间。
3. 应用偏好设置 (SharedPreferences)
SharedPreferences 是一种轻量级的存储机制,专门用于存储简单的配置数据。
3.1 获取 SharedPreferences 实例
java
// Java
// 方法一:使用应用默认的 SharedPreferences 文件
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
// 方法二:指定一个自定义的文件名(推荐)
SharedPreferences prefs = getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE);
3.2 写入数据 (Editor)
所有写入操作都必须通过 SharedPreferences.Editor 对象完成,并调用 apply() 或 commit()。
java
// Java
SharedPreferences prefs = getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
// 存储不同类型的数据
editor.putString("username", "john_doe");
editor.putInt("login_count", 5);
editor.putBoolean("is_dark_mode", true);
editor.putFloat("font_size", 14.0f);
// 提交更改
editor.apply(); // 异步提交,推荐
// editor.commit(); // 同步提交,会阻塞线程,不推荐
3.3 读取数据
java
// Java
SharedPreferences prefs = getSharedPreferences("MyAppPrefs", Context.MODE_PRIVATE);
String username = prefs.getString("username", "default_user"); // 第二个参数是默认值
int loginCount = prefs.getInt("login_count", 0);
boolean isDarkMode = prefs.getBoolean("is_dark_mode", false);
float fontSize = prefs.getFloat("font_size", 12.0f);
3.4 移除和清空数据
java
// Java
SharedPreferences.Editor editor = prefs.edit();
// 移除单个键
editor.remove("username");
// 清空所有数据
editor.clear();
editor.apply();
4. 使用场景对比
| 特性 | 应用专属文件 | SharedPreferences |
|---|---|---|
| 数据类型 | 任意格式(文本、二进制、JSON、图片等) | 仅限基本数据类型(String, int, boolean, float, long, Set) |
| 数据结构 | 无结构或自定义结构 | 键值对(Key-Value) |
| 性能 | 适合存储大量或复杂数据 | 适合存储少量、简单的配置数据 |
| 使用复杂度 | 需要手动处理文件 I/O | API 简单,开箱即用 |
| 典型用途 | 缓存图片、保存日志、存储 JSON 配置文件 | 保存用户设置、登录令牌、主题偏好、功能开关 |
5. 最佳实践与注意事项
- 优先使用内部存储 : 对于敏感或私有数据,务必使用内部存储 (
getFilesDir()),以确保数据安全。 - 不要滥用 SharedPreferences: 它不是数据库,不适合存储大量数据或复杂对象。如果需要存储复杂数据,请考虑使用 Room 数据库。
- 使用
apply()而非commit():apply()是异步的,不会阻塞主线程,性能更好。只有在极少数需要立即确认写入结果的场景才使用commit()。 - 提供合理的默认值 : 在读取
SharedPreferences时,始终提供一个有意义的默认值,以防键不存在。 - 文件命名规范 : 为文件和
SharedPreferences文件使用清晰、一致的命名,便于维护。 - 及时清理无用文件: 对于缓存文件或临时文件,应在不再需要时主动删除,避免占用用户存储空间。
- 处理外部存储权限 : 如果必须访问共享的外部存储(非应用专属目录),需要在 Android 6.0+ 上动态申请
READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE权限,并在 Android 10+ 上适配分区存储。
6. 结论
应用专属文件和 SharedPreferences 是 Android 开发中最基础、最常用的两种数据持久化方式。
- 当你需要存储结构化的配置信息或用户的简单偏好 时,
SharedPreferences是最佳选择,它简单、高效且易于使用。 - 当你需要存储任意格式的文件数据 (如文本、图片、JSON 对象等)时,应使用应用专属文件,并根据数据的隐私性和生命周期选择内部或外部存储。
正确理解和使用这两种机制,是构建健壮、可靠 Android 应用的基础。