Android 10+默认沙盒,仅自身目录,需MANAGE_EXTERNAL_STORAGE等权限及用户授权方可访问全部
安卓存储权限基础概念
存储权限分类
权限名称 | 作用范围 |
READ_EXTERNAL_STORAGE | 读取外部存储(含SD卡)的任意文件 |
WRITE_EXTERNAL_STORAGE | 向外部存储写入/删除文件 |
READ_INTERNAL_STORAGE | 读取应用安装目录下的内部存储文件(需Root权限才有效) |
WRITE_INTERNAL_STORAGE | 向应用安装目录下的内部存储写入文件(默认可写,无需额外权限) |
存储区域划分
存储类型 | 路径示例 | 特点 |
应用私有目录 | /data/data/包名/ | 仅限当前应用访问,无需存储权限 |
外部公共目录 | /sdcard/ 或 /storage/emulated/0/ | 所有应用共享,需声明存储权限 |
系统级目录 | /system/ | 仅限系统应用访问,普通应用不可读写 |
Android 10+ 存储权限变革
Scoped Storage机制
- 核心变化:应用默认只能访问自己的私有目录,访问公共目录需通过特定API。
- 受影响操作:
- 直接通过文件路径访问
/sdcard/Download/
等公共目录会失败 - 必须使用
MediaStore
或Storage Access Framework
访问媒体文件
权限行为调整
权限 | Android 10前行为 | Android 10+行为 |
READ_EXTERNAL_STORAGE | 可访问全部外部存储文件 | 仅能访问应用创建的文件和特定媒体目录 |
WRITE_EXTERNAL_STORAGE | 可写入全部外部存储 | 仅能写入应用专属目录 |
完整文件系统访问方案
传统权限申请流程(Android 10以下)
// 动态请求权限
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE
);
Android 10+兼容方案
场景 | 解决方案 |
访问任意文件 | 使用Storage Access Framework (通过DocumentFile 操作) |
管理媒体文件 | 通过MediaStore 数据库接口操作(替代文件路径访问) |
文件共享场景 | 使用ACTION_OPEN_DOCUMENT 获取持久化Uri权限 |
关键API示例
// 通过SAF选择文件
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, REQUEST_CODE);
// 通过MediaStore插入图片
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "test.jpg");
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
特殊文件访问处理
文件类型 | 访问方式 |
系统配置文件 | 需Root权限,通过Runtime.getRuntime().exec() 执行系统命令 |
其他应用安装文件 | 通过PackageManager 查询安装路径,需声明QUERY_ALL_PACKAGES 权限(Android 11+) |
缓存文件 | 优先使用应用私有缓存目录(getCacheDir() ),无需存储权限 |
权限问题诊断指南
常见错误及解决方案
现象 | 原因分析 |
openFailed: EACCES | 未获取存储权限/Android 10+访问受限目录 |
FileNotFoundException | Scoped Storage下路径访问方式错误 |
SecurityException | 尝试访问其他应用私有文件 |
调试工具推荐
- ADB命令:
adb shell ls -l /sdcard/
查看文件权限 - 日志监控:使用
Logcat
过滤TAG
为StorageManager
的日志 - 模拟器配置:在AVD设置中启用
Storage
模拟SD卡
相关问题与解答
Q1:如何将旧版存储API迁移到Android 10+?
A:
- 替换直接文件路径访问为
MediaStore
或DocumentProvider
- 使用
Context.getExternalFilesDir()
代替Environment.getExternalStorageDirectory()
- 在
AndroidManifest.xml
添加requestLegacyExternalStorage="true"
作为过渡方案
Q2:如何实现跨应用文件共享?
A:
- 通过
setIntent
附加Intent.FLAG_GRANT_READ_URI_PERMISSION
分享文件Uri - 使用
ContentResolver
的takePersistableUriPermission()
获取长期访问权限 - 确保目标应用已声明对应
intent-filter
(如category.OPENABLE