1. 分区存储背景与政策演进

1.1 引入动机:为什么需要 Scoped Storage?

在 Android 10(API 29)之前,应用只要声明了 READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE 权限,就可以访问外部存储(/sdcard)上的几乎所有文件。这种“全盘访问”模式带来三个严重问题:

  • 隐私泄露风险:恶意应用可遍历用户的照片、文档、下载目录,收集敏感数据。
  • 文件碎片化:应用卸载后残留大量无主文件,污染用户存储空间。
  • 权限滥用:大量应用申请存储权限仅为了读取自己包名下的缓存文件,却获得整个空间的访问权限。

Google 推出 Scoped Storage(分区存储),核心目的是将应用的访问范围限定在:自己的专属目录(Context.getExternalFilesDir)以及通过 MediaStore / SAF(Storage Access Framework) 授予的媒体文件。这一设计在 Android 10(可退出)→ Android 11(强制)→ Android 13(细化通知权限)逐步收紧,成为权限模型演进的里程碑。

关键痛点:旧模式下应用可以长期持有 READ_EXTERNAL_STORAGE 权限,且用户无法限制其对特定文件夹的读取。Scoped Storage 使权限变成“按需授予”,用户可以在系统界面中撤销对媒体库中某些文件的访问。

1.2 政策演进:Android 10 / 11 / 13 行为差异

下表总结了三个主要版本在分区存储上的关键行为变化:

版本 API 级别 关键变化 可退出标志
Android 10 29 首次引入分区存储,默认启用;应用仍可通过 requestLegacyExternalStorage 退回到旧模式 android:requestLegacyExternalStorage="true"
Android 11 30 分区存储变为强制,requestLegacyExternalStorage 不再生效;新增 MANAGE_EXTERNAL_STORAGE 权限用于管理目录 不可退出;需通过 MediaStore.setRequireOriginal() 等 API 适配
Android 13 33 新增细分的媒体权限:READ_MEDIA_IMAGES / READ_MEDIA_VIDEO / READ_MEDIA_AUDIO,弃用 READ_EXTERNAL_STORAGE;通知权限也被分离 —

适配陷阱:如果应用以 targetSdkVersion=29 并设置了 requestLegacyExternalStorage,在 Android 10 设备上可以正常访问旧路径,但在升级到 Android 11 后该标志失效,必须迁移到 MediaStore + SAF 方案。运行时检测版本是必要步骤。

1.3 目标用户影响范围

分区存储影响所有涉及文件读写的应用,以下三类是重灾区:

  • 文件管理类应用(如 ES 文件浏览器、MT 管理器)—— 需要 MANAGE_EXTERNAL_STORAGE 权限,并通过 Google Play 的特殊审核。
  • 媒体编辑/备份类应用(如相册、录音、网盘)—— 必须使用 MediaStore API 写入自有媒体,否则文件无法被系统媒体库索引。
  • 旧版工具类应用(targetSdk < 29 并依赖文件路径硬编码)—— 在 Android 11+ 上即使被授予存储权限,访问 /sdcard/Downloads 等公共目录也会抛出 FileNotFoundException。

从用户视角看,最直观的变化是:

  • 应用不能再“静默”读取所有照片,用户需要在系统弹窗中逐项授权。
  • 卸载应用后,其专属目录外的文件不会自动清理,但应用也无法再“扔”垃圾文件到公共目录。
  • 对系统相册的访问需要用户明确选择图片或视频,避免批量扫描。

兼容策略:如果你的应用仍需要读取公共目录中的文件(如 PDF 解析器、下载管理器),最佳实践是在 AndroidManifest.xml 中声明 MANAGE_EXTERNAL_STORAGE,并在运行时引导用户跳转到系统设置页授予“所有文件访问权限”。但该权限在 Google Play 上会受到严格审核,只有在应用核心功能确实需要时才能通过。

分区存储演进与影响范围结构图 分区存储演进与影响范围 隐私泄露风险 文件碎片化 权限滥用 Scoped Storage(分区存储) Android 10 可退出(requestLegacy) Android 11 强制分区 + MANAGE Android 13 细化媒体权限 文件管理类 需 MANAGE 审核 媒体编辑/备份 MediaStore 适配 旧版工具类 路径硬编码失效 适配重点:按需权限 + MediaStore + SAF,避免文件路径依赖 targetSdk ≥ 30 时必须迁移,否则在 Android 11+ 上直接崩溃

1.4 前置知识:沙箱 vs 公共存储

理解分区存储前必须分清两个存储区域:

  • 内部存储专属目录(沙箱):通过 Context.getFilesDir()、Context.getCacheDir() 获取,应用无需任何权限即可读写,卸载后自动清除。对应 /data/data/<package>/
  • 外部存储专属目录:Context.getExternalFilesDir(),同样无需运行时权限,卸载后清除。对应 /sdcard/Android/data/<package>/
  • 公共媒体目录:DCIM/、Pictures/、Movies/、Music/、Downloads/ 等,需要运行时权限并通过 MediaStore 或 SAF 操作。

最佳实践:面向 Android 10+ 开发时,所有用户可见的文件(如截图、导出文件)都应该通过 MediaStore 写入公共媒体目录,并正确设置 IS_PENDING 标志以避免文件被其他应用提前读取到不完整的内容。

1.5 章节小结

本章建立了分区存储的政策背景和版本分水岭:Android 10 为“过渡期”,Android 11 为“强制期”,Android 13 为“精细化期”。核心动机是保护用户隐私、减少文件碎片、规范权限模型。受影响的应用必须从路径依赖迁移到 MediaStore + SAF 架构,否则在 Android 11+ 上将面临文件访问失败、数据丢失甚至审核被拒的风险。接下来的章节将逐一深入实现流程、代码适配、调试工具和常见错误的解决方案。