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

Android 10及以下真机使用PhotoPicker API获取持久URI权限遇异常

Android 10及以下真机PhotoPicker回退模式下持久化权限异常解决方案

问题重现

在Android 10(API29)及以下真机(如三星Galaxy S9、红米9)上使用PhotoPicker API时,系统会回退到ACTION_OPEN_DOCUMENT Intent。此时调用takePersistableUriPermission获取返回URI的持久化权限,会抛出如下异常:

java.lang.SecurityException: No persistable permission grants found for UID 10574 and Uri 0 @ content://media/external/file/2

相关实现代码:

// Code from Fragment
val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    if (uri != null) {
        val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
        requireContext().contentResolver.takePersistableUriPermission(uri, flag)
    } else {
        Log.d("PhotoPicker", "No media selected")
    }
}

pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

该问题仅在真机出现,模拟器运行正常。

问题原因

Android系统中,只有来自DocumentsProvider的URI(如content://com.android.providers.downloads.documents/...)支持调用takePersistableUriPermission获取持久化权限。而PhotoPicker在API29及以下设备回退到ACTION_OPEN_DOCUMENT时,部分厂商系统返回的是MediaStore的URI(如content://media/external/file/...),这类URI不支持持久化权限操作,因此抛出异常。

模拟器的系统实现可能默认返回DocumentsProvider的URI,所以不会触发该问题。

解决方案

方案1:捕获异常并区分处理

直接捕获SecurityException,针对MediaStore URI采用常规权限访问:

val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    if (uri != null) {
        try {
            val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
            requireContext().contentResolver.takePersistableUriPermission(uri, flag)
            Log.d("PhotoPicker", "持久化权限获取成功")
        } catch (e: SecurityException) {
            Log.d("PhotoPicker", "当前URI不支持持久化权限,使用常规权限访问", e)
            // API29及以下需确保已申请READ_EXTERNAL_STORAGE权限
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
                if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED
                ) {
                    ActivityCompat.requestPermissions(
                        requireActivity(),
                        arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                        REQUEST_CODE_STORAGE_PERMISSION
                    )
                }
            }
        }
    } else {
        Log.d("PhotoPicker", "未选择媒体文件")
    }
}

pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

方案2:提前判断URI类型

通过DocumentsContract.isDocumentUri()判断URI是否来自DocumentsProvider,仅对支持的URI申请持久化权限:

val pickMedia = registerForActivityResult(PickVisualMedia()) { uri ->
    if (uri != null) {
        if (DocumentsContract.isDocumentUri(requireContext(), uri)) {
            // 仅对DocumentsProvider URI申请持久化权限
            val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
            requireContext().contentResolver.takePersistableUriPermission(uri, flag)
            Log.d("PhotoPicker", "持久化权限获取成功")
        } else {
            // MediaStore URI,依赖READ_EXTERNAL_STORAGE权限(API29及以下)
            Log.d("PhotoPicker", "当前为MediaStore URI,使用常规权限访问")
            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
                // 权限检查逻辑同方案1
            }
        }
    } else {
        Log.d("PhotoPicker", "未选择媒体文件")
    }
}

pickMedia.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

补充说明

  • API29及以下,MediaStore的文件URI只要持有READ_EXTERNAL_STORAGE权限,应用重启后仍可正常访问,无需持久化权限。
  • API30及以上,系统采用分区存储,无需申请READ_EXTERNAL_STORAGE权限即可访问MediaStore中的媒体文件,也不存在该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:44:54