Android 10不使用requestLegacyExternalStorage从相册获取图片的正确方法
问题根因
当前使用ACTION_PICK拉起相册时,部分第三方相册(如谷歌相册)会返回封装后的代理URI,系统解析跳转到底层媒体库URI时,应用未拿到对应媒体文件的临时访问授权,因此抛出FileNotFoundException验证失败错误。
符合Android 10+分区存储规范的实现方案
1. 权限配置优化
Android 10及以上通过系统选择器选取图片时,无需申请存储权限即可拿到URI的临时访问权,可将原有权限配置调整为仅对低版本设备生效:
<!-- 仅Android 9及以下需要存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
原有queries配置无需修改。
2. 相册选择器启动代码调整
放弃使用ACTION_PICK,改用官方推荐的ACTION_GET_CONTENT拉起选择器,返回的URI直接携带有效临时访问权限,不会出现代理URI解析失败问题:
val intent = Intent(Intent.ACTION_GET_CONTENT) intent.type = "image/*" // 限制仅返回可直接打开的有效URI intent.addCategory(Intent.CATEGORY_OPENABLE) startActivityForResult(Intent.createChooser(intent, "选择图片"), REQUEST_CODE_SELECT_IMAGE)
推荐使用更稳定的Activity Result API替代已废弃的onActivityResult方法,不调整回调方法也可正常运行。
3. 结果回调逻辑优化
增加异常捕获逻辑,避免无效URI直接导致崩溃,读取到流后建议先拷贝到应用私有目录再执行上传操作,避免临时URI失效导致后续操作失败:
data.data?.let { uri -> Log.d(TAG, uri.toString()) try { requireContext().contentResolver.openInputStream(uri)?.use { stream -> // 示例:将图片流拷贝到应用私有缓存目录 val cacheFile = File(requireContext().cacheDir, "upload_temp.jpg") cacheFile.outputStream().use { output -> stream.copyTo(output) } // 后续使用cacheFile的路径执行上传操作 } ?: run { // 流为空时的异常处理 } } catch (e: Exception) { e.printStackTrace() // 读取失败时的异常处理 } }
补充说明
- 若有直接扫描系统相册、无需用户主动选图的需求,Android 13及以上需申请
READ_MEDIA_IMAGES权限,Android 10-12需申请READ_EXTERNAL_STORAGE权限。 - 不要长期存储通过选择器获取的URI,其临时访问权限会在应用重启后失效。
内容的提问来源于stack exchange,提问作者user-44651
相关产品推荐
相关产品推荐

