android 11 及以上版本因分区存储(scoped storage)限制,使用 `action_pick` + `extra_output` 方式会导致“access denied”错误;应改用 `action_get_content` 获取只读 uri,并通过 `contentresolver` 安全读取图像数据。
在 Android 11(API 30)及更高版本中,Google 强制推行 分区存储(Scoped Storage),大幅收紧对共享存储(如 DCIM、Pictures)的直接文件路径访问权限。你当前代码中使用 Intent.ACTION_PICK 并传入 EXTRA_OUTPUT 的方式,在 Android 11+ 上已被系统拒绝写入权限——即使你声明了 READ_MEDIA_IMAGES,MediaStore 也不再允许第三方 App 向外部 URI 写入临时结果,因此会触发 “Access denied” 错误。
✅ 正确做法:放弃 ACTION_PICK + EXTRA_OUTPUT,改用 ACTION_GET_CONTENT
该方式不尝试写入外部 URI,而是由系统返回一个具有临时读取权限的 content:// URI(如 content://media/…),App 可通过 ContentResolver 安全读取图像数据,完全符合 Android 11+ 的隐私与存储规范。
✅ 推荐实现(现代 AndroidX Activity Result API)
// Kotlin 示例(推荐)
private val pickerActivityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK && result.data?.data != null) {
val photoUri: Uri = result.data!!.data!!
// ✅ 安全获取 Bitmap(无需文件路径!)
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, photoUri)
imageView.setImageBitmap(bitmap)
// ✅ 或转换为 File(如需保存/上传)
val inputStream = contentResolver.openInputStream(photoUri)
val tempFile = File(cacheDir, "picked_${System.currentTimeMillis()}.jpg")
inputStream?.use { input ->
FileOutputStream(tempFile).use { output ->
input.copyTo(output)
}
}
Log.d("Picker", "Saved to: ${tempFile.absolutePath}")
}
}
// Java 示例(兼容写法)
private final ActivityResultLauncher<Intent> pickerActivityResultLauncher =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
Uri photoUri = result.getData().getData();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(
getContentResolver(), photoUri);
imageView.setImageBitmap(bitmap);
// 如需保存为本地文件(推荐缓存目录)
InputStream is = getContentResolver().openInputStream(photoUri);
File tempFile = new File(getCacheDir(), "picked_" + System.currentTimeMillis() + ".jpg");
try (FileOutputStream os = new FileOutputStream(tempFile)) {
is.transferTo(os);
}
Log.d("Picker", "Saved: " + tempFile.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
});
调用选取器: