

























以下是对 Android 导出权限与存储目录 的系统性总结笔记,涵盖权限要求、适用场景及代码示例。
一、Android 存储目录分类
目录 路径示例 特点
内部存储 /data/data/包名/files 空间有限,其他应用不可见,卸载时删除
内部缓存 /data/data/包名/cache 同上,系统可能自动清理
外部私有目录 /storage/emulated/0/Android/data/包名/files 空间较大,无需权限,卸载时删除
外部私有缓存 /storage/emulated/0/Android/data/包名/cache 同上,系统可能自动清理
获取方式:
// 内部存储
context.filesDir
context.cacheDir
// 外部私有目录(推荐用于导出文件)
context.getExternalFilesDir(null) // 根目录
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) // 分类子目录(仅逻辑分类)
权限要求:无(Android 4.4+)
目录 路径示例 访问方式
Downloads /storage/emulated/0/Download MediaStore API 或 SAF
Documents /storage/emulated/0/Documents MediaStore API 或 SAF
DCIM/Camera /storage/emulated/0/DCIM MediaStore API
Pictures /storage/emulated/0/Pictures MediaStore API
外部存储根目录 /storage/emulated/0 旧版用 WRITE_EXTERNAL_STORAGE(已废弃)
权限要求:
· Android 10 及以下:需 WRITE_EXTERNAL_STORAGE 权限(动态申请)
· Android 11+:无法直接使用文件路径写入,必须通过 MediaStore 或 SAF
二、权限演进历史
Android 版本 特性 说明
4.4 (API 19) 引入外部私有目录免权限 getExternalFilesDir() 无需任何权限
6.0 (API 23) 运行时权限 WRITE_EXTERNAL_STORAGE 需动态申请
10 (API 29) 分区存储(Scoped Storage) 应用只能访问自身私有目录和 MediaStore 中的公共文件
11 (API 30) 强制分区存储 即使有权限也不能直接访问公共目录的文件路径
13 (API 33) 细化媒体权限 新增 READ_MEDIA_IMAGES/AUDIO/VIDEO,READ_EXTERNAL_STORAGE 弱化
三、导出文件的推荐方案
方案 1:写入外部私有目录 + 分享(零权限,推荐)
// 1. 写入
val file = File(context.getExternalFilesDir(null), "export.xlsx")
file.writeBytes(data)
// 2. 分享
val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
val intent = Intent(Intent.ACTION_SEND).apply {
type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
putExtra(Intent.EXTRA_STREAM, uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(Intent.createChooser(intent, "分享文件"))
需要配置 FileProvider(res/xml/file_paths.xml):
<paths>
<external-files-path name="exports" path="." />
</paths>
方案 2:通过 MediaStore 保存到 Downloads(Android 10+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver = context.contentResolver
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "export.xlsx")
put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
}
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
uri?.let {
resolver.openOutputStream(it)?.use { outputStream ->
outputStream.write(data)
}
}
}
权限要求:无需 WRITE_EXTERNAL_STORAGE,但会弹出系统授权框(仅首次)
方案 3:旧版直接写公共目录(仅支持 Android 10 以下)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
// 需先动态申请 WRITE_EXTERNAL_STORAGE
val file = File(Environment.getExternalStorageDirectory(), "Download/export.xlsx")
file.writeBytes(data)
}
四、权限检查清单
操作 权限 备注
读写 getExternalFilesDir() 无 推荐
读写 context.filesDir 无 内部存储
使用 MediaStore 写入公共目录 无危险权限 需 ContentValues 配置
直接读写公共目录路径(Android 10 以下) WRITE_EXTERNAL_STORAGE 需动态申请
直接读写公共目录路径(Android 11+) 不可能 系统禁止,只能用 MediaStore/SAF
五、最佳实践总结
六、快速决策流程图
是否需要用户直接访问文件(如用文件管理器打开)?
├─ 不需要 → 写入 getExternalFilesDir() + 用 FileProvider 分享给其他 App
│ ✅ 无需任何权限
│
└─ 需要(如保存到 Downloads)
├─ 最低 API >= 29(Android 10+)→ 使用 MediaStore
│ ✅ 无需危险权限,仅需系统授权
│
└─ 需支持 API <= 28(Android 9 及以下)
├─ 放弃直接存 Downloads,改用分享方案
└─ 或动态申请 WRITE_EXTERNAL_STORAGE(不推荐)
这套笔记涵盖了从权限到目录到代码的全链路,以后遇到导出需求可以直接参考。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。