使用Android存储访问框架(SAF)为何仍需READ_EXTERNAL_STORAGE权限?
解决SAF访问图片时未申请
READ_EXTERNAL_STORAGE权限崩溃的问题 这问题我之前帮不少开发者排查过,核心是你对SAF的权限逻辑有个小误区——不是所有SAF场景都能完全绕开READ_EXTERNAL_STORAGE权限,得结合Android版本和你的代码实现来看:
为什么会崩溃?
常见的触发原因有这几个:
- Android版本兼容性问题:Android 10(API 29)是个分界点,在这之前的系统里,SAF并没有完全独立于传统存储权限体系。哪怕你通过SAF让用户选了文件,系统依然可能会检查应用是否持有
READ_EXTERNAL_STORAGE,没有的话就会抛出SecurityException导致崩溃。 - 混用SAF Uri和传统File API:很多人拿到SAF返回的Uri后,会尝试把它转成真实的本地文件路径(比如用
MediaStore查询或者第三方工具类),这一步本质是在直接访问传统存储区域,必然会触发权限检查,而SAF的授权并不覆盖这个操作。 - 访问特殊目录限制:如果你的SAF操作涉及SD卡上的非公开目录、系统目录,哪怕用户手动选了,部分系统也可能需要额外权限或者直接限制访问。
具体解决方案
1. 针对Android 10+(API 29及以上)
严格遵循SAF的操作规范,全程用Uri和ContentResolver操作文件,绝对不要碰“转路径”的操作:
- 加载图片的正确示例:
// 拿到用户通过SAF选择的图片Uri后 val inputStream = contentResolver.openInputStream(selectedImageUri) val bitmap = BitmapFactory.decodeStream(inputStream) yourImageView.setImageBitmap(bitmap) inputStream?.close()
- 如果用图片加载库(比如Glide、Coil),直接传入SAF Uri即可,主流库都原生支持SAF,不需要额外配置。
2. 针对Android 9及以下(API 28及以下)
没办法绕开传统权限,必须:
- 在
AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- 针对API 23+(Android 6.0及以上),还要在运行时动态申请这个权限,用户同意后再使用SAF。
3. 排查代码中的隐藏触发点
检查是否有第三方库、工具类在偷偷把SAF Uri转成File路径——比如某些老版本的图片处理库、文件操作工具,遇到这种情况要么升级库版本,要么替换成SAF兼容的实现。
总结
SAF的核心优势是让用户授权特定文件/目录,而非全局存储权限,但它不是完全脱离传统权限体系的“万能钥匙”。只要严格按SAF的规范操作(全程用Uri和ContentResolver),在Android 10+确实可以不用READ_EXTERNAL_STORAGE;但低版本系统必须配合传统权限,混用API也会触发权限检查导致崩溃。
内容的提问来源于stack exchange,提问作者Aniket Singh




