Android11移除MANAGE_ALL_FILES权限后MediaProvider读文件报错求助
解决方案
报错根因
收到权限报错的核心原因是:通过SAF(存储访问框架)拿到的DocumentFile Uri本身自带授予的访问权限,但调用MediaStore.getMediaUri()将其转换为MediaStore专属Uri后,系统并未给应用授予对应MediaStore文件的访问权限,因此触发了权限拒绝。
另外当前的SAF请求逻辑缺少持久化权限的配置,重启应用后之前拿到的目录访问权限会失效。
修复步骤
- 调整SAF发起请求时的Flag,增加持久化权限授权标记
- 拿到目录Uri后主动申请持久化权限,避免权限丢失
- 直接使用SAF返回的DocumentFile Uri操作文件,无需转换为MediaStore Uri,SAF本身的Uri完全支持读取内容、加载到ImageView、传递给其他组件等操作
- 额外添加数组越界判断,避免目录下文件不足10个时崩溃
修复后代码
fun opendd(){ val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { // 同时添加读权限和持久化权限标记 flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION } startActivityIfNeeded(i, 33) } // onActivityResult 逻辑 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { 33 -> { if (resultCode == RESULT_OK) { data?.data?.also { uri -> // 申请持久化权限,确保重启应用后仍能访问该目录 contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) val dfile = DocumentFile.fromTreeUri(applicationContext, uri) val fileList = dfile?.listFiles() ?: emptyArray() Log.d("usr", fileList.contentToString()) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && fileList.size >= 10) { // 直接使用SAF返回的Uri,无需转换为MediaStore Uri val i = Intent(this, AnimatedImageView::class.java) i.data = fileList[9].uri startActivity(i) } } } } } }
额外说明
如果需要遍历的目录下文件数量较多,DocumentFile.listFiles()性能较差,可直接通过ContentResolver查询DocumentsContract的相关列获取文件列表,效率会提升数倍。
你无需申请MANAGE_ALL_FILES权限,也不需要申请READ_EXTERNAL_STORAGE/READ_MEDIA_*等权限,仅靠SAF授予的目录权限即可完成所有文件操作,完全符合Google Play政策要求。
内容的提问来源于stack exchange,提问作者Jose Escoto
相关产品推荐
相关产品推荐

