卸载重装后Android应用访问Picker URI触发SecurityException问题
问题原因与解决方案
核心问题
你遇到的SecurityException本质是:系统媒体选择器(PhotoPicker)返回的content://media/picker/...类型Uri是临时中转Uri,并非真实的MediaStore资源Uri。这类Uri的权限与当前应用的UID绑定,卸载重装后应用UID会变更,之前通过takePersistableUriPermission申请的持久权限直接失效,自然无法访问。
解决步骤
必须将Picker返回的中转Uri转换为真实的MediaStore原生Uri,再对真实Uri申请持久权限,才能跨卸载重装保留访问能力。
1. 转换Picker Uri为真实MediaStore Uri
通过ContentResolver查询Picker Uri,获取媒体文件的真实ID,再构建对应的MediaStore Uri:
fun Context.getRealMediaUri(pickerUri: Uri): Uri? { // 根据媒体类型选择对应的Projection,图片用Images,视频用Video val projection = arrayOf(MediaStore.Images.Media._ID) contentResolver.query( pickerUri, projection, null, null, null )?.use { cursor -> if (cursor.moveToFirst()) { val mediaId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)) // 构建真实的MediaStore图片Uri return ContentUris.withAppendedId( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediaId ) } } return null }
2. 修改权限申请逻辑
用户选择图片后,先转换Uri,再对真实Uri申请持久权限:
// 处理图片选择结果的回调 fun handleSelectedImage(pickerUri: Uri) { val realMediaUri = getRealMediaUri(pickerUri) ?: return // 对真实Uri申请持久访问权限 persistUriAccess(realMediaUri) // 将realMediaUri保存到本地(比如SharedPreferences、Room数据库) saveUriToLocal(realMediaUri.toString()) } // 原有的持久权限申请函数可以保留 fun Context.persistUriAccess(uri: Uri) { grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) }
3. 卸载重装后的访问逻辑
重装应用后,先申请对应的媒体权限(你之前写的accessGallery()逻辑可以保留),然后读取本地保存的真实MediaStore Uri,直接访问即可——因为之前已经对该Uri申请了持久权限,只要文件未被删除,就能正常访问。
补充说明
READ_MEDIA_VISUAL_USER_SELECTED权限仅允许访问用户手动选择过的媒体,但它依赖应用UID,卸载重装后同样失效,无法替代持久Uri权限。- 不要试图依赖固定应用UID来规避问题,这会导致签名、多用户场景下的兼容性问题,属于不规范操作。
内容的提问来源于stack exchange,提问作者Ivan Morgillo
相关产品推荐
相关产品推荐

