Android API 30下使用SAF创建文件后分享报SecurityException权限错误如何解决
问题原因分析
- 你拿到的SAF URI属于DocumentsProvider的内容URI,
FLAG_GRANT_READ_URI_PERMISSION默认仅对应用自身包名下的ContentProvider(比如FileProvider)生效,跨包的DocumentsProvider URI需要主动申请临时权限授予 - 调用SAF选择器拿到URI后,仅当前应用获得了读写权限,系统不会自动帮你把读取权限透传给第三方分享应用
- 部分ROM的DocumentsProvider实现存在差异,即使是应用自身创建的文件,也不会默认对第三方应用开放读取权限
- 额外说明:API 30并未废弃FileProvider,仅限制了FileProvider对外界公共存储目录的访问,应用私有目录/缓存目录的FileProvider分享逻辑完全可用
解决方案
方案1:手动授予URI临时权限(优先推荐)
构造分享意图时,除了添加FLAG标识,还需要主动给所有匹配到的分享目标应用授予临时读取权限:
// 替换yourMimeType为实际分享的文件类型,fileUri为SAF返回的文件URI Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType(yourMimeType); shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 遍历所有匹配到的分享应用,逐个授予权限 List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(shareIntent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); } startActivity(Intent.createChooser(shareIntent, "分享文件"));
分享完成后可调用revokeUriPermission回收权限,避免权限泄露
方案2:写入公共媒体目录分享
如果分享的是图片、视频、音频这类公共媒体文件,可直接写入MediaStore公共目录,拿到MediaStore URI后即可直接分享,无需额外申请权限,适配逻辑更简单。
方案3:缓存中转分享
若上述方案不生效,可先将SAF路径下的文件读取拷贝到应用私有缓存目录,再用原有FileProvider方案实现分享,该逻辑在API 30+上完全兼容。
排查注意事项
- 拿到SAF URI后需调用
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)持久化权限,避免应用重启后权限丢失 - 全程使用URI操作文件,不要将SAF URI转换为本地绝对路径使用,不同ROM下路径规则不通用
- 分享前可先调用
getContentResolver().openInputStream(uri)测试自身是否有读取权限,排除文件写入失败的问题
内容的提问来源于stack exchange,提问作者donny.rewq
相关产品推荐
相关产品推荐

