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

如何通过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,提问作者Сергей В

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:22:44