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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 18:54:00