Storage Access Framework — 是一个 Android 框架,用于通过系统选择对话框安全访问文件和文档。应用无需请求广泛的存储读取权限,而是获取用户所选文件的 URI 链接。根据 Android Developers (2026),SAF 通过 DocumentProvider 支持本地存储、Google Drive 和 Dropbox 云服务。该框架在 Android 4.4 中引入,并从 Android 10 开始成为文档处理的标准。
要点
Storage Access Framework — 是 Android SDK 的一部分,提供通过系统对话框选择和创建文档的统一界面。SAF 抽象了文件的物理位置:应用不知道文件从哪里读取 — 从本地磁盘、Google Drive 还是 Dropbox。
SAF 的架构包括三个组件:发起请求的客户端应用、标准文件选择对话框 — 系统选择器,以及实现特定存储文件访问的DocumentProvider 内容提供者。用户无论数据来源如何,都能看到统一的界面。
SAF 从 Android 4.4 (API 19) 开始可用。最初该框架仅支持文档,但在 Android 5.0 中通过 ACTION_OPEN_DOCUMENT_TREE 添加了对目录的支持。从 Android 10 开始,SAF 成为访问应用专属存储外任何文件的推荐方式。
SAF 通过 Intent 使用 ACTION_OPEN_DOCUMENT 动作启动系统文件选择对话框。用户可以看到可用来源列表:内部存储、Google Drive、Dropbox 和其他注册的提供者。选择文件后,系统返回一个 content URI 并附带临时访问权限。
Content URI 的格式为 content://com.android.externalstorage.documents/document/primary/Download/file.pdf。与 file:// URI 不同,content URI 不透露文件的真实路径,并通过 DocumentProvider 工作。应用通过 ContentResolver.openInputStream 基于获取的 URI 读取文件。
系统选择器自动支持按 MIME 类型搜索、排序和过滤文件。开发者通过 Intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("application/pdf", "image/*")) 指定需要的类型,选择器仅显示相关文件。
默认情况下,URI 在应用重启或设备重启前有效。对于长期访问,应用请求 takePersistableUriPermission — 这允许在后续启动时读取文件,而无需通过对话框重新选择。
SAF 提供三种主要文件操作:打开、创建和选择文件夹树。ACTION_OPEN_DOCUMENT 打开系统选择器以选择现有文件。ACTION_CREATE_DOCUMENT 创建新文件 — 用户在对话框中输入名称,应用获取 URI 进行写入。
ACTION_OPEN_DOCUMENT_TREE 允许选择整个目录并访问其中的所有文件。它在文件管理器、备份和同步应用中使用。应用获取目录的 URI,并可以通过 DocumentFile 递归枚举内容。
| 动作 | 目的 | 标志 |
|---|---|---|
| ACTION_OPEN_DOCUMENT | 选择现有文件 | READ_FLAG |
| ACTION_CREATE_DOCUMENT | 创建新文件 | WRITE_FLAG |
| ACTION_OPEN_DOCUMENT_TREE | 选择目录 | READ + WRITE |
对于每个动作,MIME 类型通过 EXTRA_MIME_TYPES 指定。可以指定多个类型:"application/pdf","image/*","text/plain"。如果未指定类型,选择器显示所有文件。
Persistent URI permissions — 是一种 SAF 机制,允许在当前会话结束后保持对文件的访问。没有此标志,URI 仅在应用重启前有效。应用请求 FLAG_GRANT_PERSISTABLE_URI_PERMISSION 标志以及 READ 和 WRITE 标志。
为保存权限,使用 ContentResolver.takePersistableUriPermission 方法。系统将 URI 存储在特殊注册表中,并在应用下次启动时恢复访问。要检查应用是否具有对 URI 的持久访问,使用 ContentResolver.persistedUriPermissions。
用户可以通过设置 → 应用 → 权限撤销已保存的权限。应用应处理文件访问意外消失的场景。建议在开始工作前通过 ContentResolver.openInputStream 检查访问权限,并正确处理 SecurityException。
SAF 通过标准 Intent 启动。代码最小化:创建一个带有 ACTION_OPEN_DOCUMENT 的 Intent,指定 MIME 类型,并通过 ActivityResultContracts.StartActivityForResult 启动。选择文件后,回调在 data.data 中获取 URI,然后用于读取。
val pickFile = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
result.data?.data?.let { uri ->
val inputStream = contentResolver?.openInputStream(uri)
// read from inputStream
}
}
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
setType("application/pdf")
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
pickFile.launch(intent)
要写入新文件,请使用 ACTION_CREATE_DOCUMENT。Intent 接受 MIME 类型并通过 EXTRA_TITLE 接受文件名。选择文件夹并确认后,用户返回一个 URI,应用通过 ContentResolver.openOutputStream 将数据写入其中。系统会自动在选定位置创建文件。
SAF 与传统 READ_EXTERNAL_STORAGE 的区别在于安全性的根本不同方法。"一次性"请求整个存储访问,而是仅请求特定文件的访问。这最小化了攻击面并提高了用户信任。
READ_EXTERNAL_STORAGE 允许访问设备上的所有文件,包括其他应用的照片和文档。在 Android 10+ 上,这种方法受到限制。相反,SAF 仅提供对用户所选文件的访问,并要求每个文件明确确认。批量处理媒体请使用 MediaStore,单个文档使用 SAF。
SAF 的主要缺点是需要单独选择每个文件。对于批量操作(备份、文件夹同步),使用 ACTION_OPEN_DOCUMENT_TREE,它提供对所选目录中所有文件的访问。解决方案取决于场景:SAF 适用于用户选择,MediaStore 适用于对媒体的编程访问。
常见问题
Storage Access Framework — 是一个 Android 框架,用于通过系统选择对话框安全访问文件。应用无需请求广泛的存储读取权限即可获取所选文件的 content URI。
创建带有 ACTION_OPEN_DOCUMENT 的 Intent,通过 setType 指定 MIME 类型,并通过 ActivityResultContracts 启动。在回调中从 data.data 获取 URI,并通过 ContentResolver.openInputStream 读取。
Persistent URI permissions — 是在应用重启后保持 URI 访问的机制。通过 FLAG_GRANT_PERSISTABLE_URI_PERMISSION 标志和 takePersistableUriPermission 方法请求。
SAF 支持任何 MIME 类型:application/pdf、image/*、text/plain、application/zip 等。类型通过 Intent.putExtra 与 EXTRA_MIME_TYPES 数组指定。
SAF 旨在让用户通过系统对话框选择单个文件。MediaStore 通过媒体数据库提供对设备所有媒体文件的编程访问,无需用户参与。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。