部分设备使用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
相关产品推荐
相关产品推荐

