You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部分设备使用Storage Access Framework触发SecurityException问题求助

问题分析

在Vivo、Oppo及新款三星的Android 11/12设备上,通过SAF(存储访问框架)选择文件夹后遍历文件时,部分返回的文件Uri属于MediaStore而非SAF树Uri,而应用仅持有SAF树的访问权限,因此访问这类Uri会触发SecurityException,导致Glide加载失败、文件复制出错。

解决方案

1. 修正权限申请逻辑

移除不必要的grantUriPermission调用,takePersistableUriPermission已足够为SAF树Uri持久化权限:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if(resultCode == Activity.RESULT_OK && requestCode == 1088) {
        data?.data?.let { selectedDirUri ->
            val takeFlags = data.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            contentResolver.takePersistableUriPermission(selectedDirUri, takeFlags)
            // 将selectedDirUri.toString()持久化存储,后续遍历使用
        }
    }
}

2. 确保使用SAF树Uri访问文件

遍历文件时严格基于所选根目录的SAF树Uri构建文档Uri,避免获取MediaStore类型的Uri:

  • 保留原代码中DocumentsContract.buildDocumentUriUsingTree(rootUri, docId)的逻辑,同时打印newNode确认格式应为content://com.android.externalstorage.documents/tree/xxx/document/xxx
  • 若仍出现MediaStore Uri,可在遍历过滤时添加条件,仅保留SAF树内的文档:
val c = contentResolver.query(
    childrenUri,
    arrayOf(
        DocumentsContract.Document.COLUMN_DOCUMENT_ID,
        DocumentsContract.Document.COLUMN_DISPLAY_NAME,
        DocumentsContract.Document.COLUMN_MIME_TYPE,
        DocumentsContract.Document.COLUMN_LAST_MODIFIED
    ),
    // 过滤仅保留当前SAF树下的文档
    "${DocumentsContract.Document.COLUMN_PARENT_DOCUMENT_ID} = ?",
    arrayOf(DocumentsContract.getTreeDocumentId(rootUri)),
    null
)

3. 版本兼容处理FLAG

FLAG_GRANT_PREFIX_URI_PERMISSION是Android 12(API 31)新增权限,需做版本判断:

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
var flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
            Intent.FLAG_GRANT_READ_URI_PERMISSION or
            Intent.FLAG_GRANT_WRITE_URI_PERMISSION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    flags = flags or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
}
intent.addFlags(flags)
startActivityForResult(intent, 1088)

4. Glide加载调整

直接传递SAF Uri对象给Glide,而非转换为字符串:

Glide.with(context)
    .load(newNode) // 传入Uri对象而非字符串
    .into(imageView)

5. 替代方案(针对公共媒体目录)

若用户选择的是DCIM、Pictures等公共媒体目录,可结合MediaStore查询:

  • Android 11-12申请READ_EXTERNAL_STORAGE权限
  • Android 13+申请READ_MEDIA_IMAGES/READ_MEDIA_VIDEO权限
  • 通过MediaStore直接查询对应目录下的媒体文件

内容的提问来源于stack exchange,提问作者Smeet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 23:40:27