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
相关产品推荐
相关产品推荐

