如何通过PickVisualMedia()获取可持久化URI权限?API>29遇权限异常
解决Android API>29图库URI持久化权限SecurityException问题
问题场景
- 应用调用手机图库选图,把获取到的URI存进数据库,后续调用这个URI时触发权限异常:
java.lang.SecurityException: Calling uid ( 10192 ) does not have permission to access picker uri: content://media/picker/0/com.android.providers.media.photopicker/media/1000000033
- 应用启动时已经申请了以下权限:
val permissionState = rememberMultiplePermissionsState( listOf( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA ) ) SideEffect() { permissionState.launchMultiplePermissionRequest() }
- 调用下面的方法加载图片时还是报错:
fun getImgBitmap(img: String, context: Context): Bitmap { val source: ImageDecoder.Source val bitmap: Bitmap source = ImageDecoder.createSource(context.contentResolver, Uri.parse(img)) bitmap = ImageDecoder.decodeBitmap(source) ..... }
问题仅出现在API > 29版本中
尝试方案及新报错
针对API>29版本,尝试获取持久化权限,但触发新的SecurityException:
val contentResolver = context.contentResolver val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION contentResolver.takePersistableUriPermission(Uri.parse(img), takeFlags) source = ImageDecoder.createSource(contentResolver, Uri.parse(img)) bitmap = ImageDecoder.decodeBitmap(source)
java.lang.SecurityException: No persistable permission grants found for UID 10195 and Uri content://media/...
正确解决方法
1. 改用支持持久化的图库选择Intent
放弃旧的ACTION_GET_CONTENT,使用ACTION_OPEN_DOCUMENT发起图库选择,这个Intent返回的URI支持持久化权限:
// Jetpack Compose 示例 val pickImageLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> uri?.let { // 处理选中的URI } } // 触发选图操作 pickImageLauncher.launch(arrayOf("image/*"))
2. 在选图结果中申请持久化权限
拿到选图返回的URI后,先临时授予权限,再转为持久化权限,之后再把URI存进数据库:
val pickImageLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> uri?.let { val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION // 申请持久化权限 context.contentResolver.takePersistableUriPermission(it, takeFlags) // 将uri.toString()存入数据库,后续随时可用 } }
3. 为什么之前的尝试失败?
- 用
ACTION_GET_CONTENT拿到的picker URI(格式是content://media/picker/...)不支持持久化权限,只能在当前会话临时使用,应用重启后权限直接失效。 - 只有通过
ACTION_OPEN_DOCUMENT或ACTION_OPEN_DOCUMENT_TREE获取的URI,才能调用takePersistableUriPermission申请持久化权限。 - API>29之后,
READ_EXTERNAL_STORAGE权限不再直接赋予媒体文件的持久访问权,必须通过文件选择框架的持久化URI机制来实现。
4. 版本兼容处理
如果需要兼容API<=29的设备,可以做版本判断:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 使用ACTION_OPEN_DOCUMENT + 持久化权限方案 } else { // 使用ACTION_GET_CONTENT + READ_EXTERNAL_STORAGE权限方案 }
内容的提问来源于stack exchange,提问作者Сергей В
相关产品推荐
相关产品推荐

