在RecyclerView Adapter中加载URI图片遇权限拒绝问题求助
问题:从URI加载图片在Activity正常,RecyclerView.Adapter中出现权限拒绝
- 场景:通过用户选择的图片URI,在Activity中加载正常,但在Fragment关联的RecyclerView.Adapter的
onBindViewHolder方法中加载时抛出权限拒绝错误 - 补充:该URI在与选择图片的Activity无关的其他Activity中也能正常加载
错误信息
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{e8f7e7f 20114:my.package.name/u0a290} (pid=20114, uid=10290) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
当前相关代码
图片加载代码
user_image = findViewById(R.id.user_image); String path = sharedPreferences.getString("imagePath", "default"); if (path.equals("default")) { user_image.setImageResource(R.drawable.profile); } else { user_image.setImageURI(Uri.parse(path)); }
图片选择及路径保存代码
Intent i = new Intent(); i.setType("image/*"); i.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(i, "Select Picture"), IMAGE_REQUEST_CODE);
OnActivityResult代码
Uri selectedImageUri = data.getData(); SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("USERDATA", MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); if (selectedImageUri != null) { image_view.setImageURI(selectedImageUri); imagePath = selectedImageUri.toString(); editor.putString("imagePath", imagePath); editor.apply(); }
原因及解决方案
核心原因
使用ACTION_GET_CONTENT获取的URI仅拥有临时访问权限,该权限仅在发起请求的Activity生命周期内有效。后续在Adapter等其他组件中使用该URI时,临时权限已失效,因此触发权限拒绝错误。你提到的其他Activity能正常加载,只是系统权限缓存未清除的偶然情况,并非可靠行为。
解决方法
方法1:改用ACTION_OPEN_DOCUMENT获取持久化权限
这是Android官方推荐的方案,通过ACTION_OPEN_DOCUMENT获取URI并申请持久化权限,确保后续任何组件都能正常访问:
// 修改图片选择Intent Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT); i.setType("image/*"); i.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(Intent.createChooser(i, "Select Picture"), IMAGE_REQUEST_CODE); // 修改OnActivityResult代码,添加权限持久化逻辑 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == IMAGE_REQUEST_CODE && resultCode == RESULT_OK && data != null) { Uri selectedImageUri = data.getData(); if (selectedImageUri != null) { // 申请持久化读写权限 final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); requireActivity().getContentResolver().takePersistableUriPermission(selectedImageUri, takeFlags); // 保存URI到SharedPreferences SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("USERDATA", MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("imagePath", selectedImageUri.toString()); editor.apply(); image_view.setImageURI(selectedImageUri); } } }
方法2:将图片复制到应用私有目录后保存路径
如果不需要保留原图位置,可以将选中的图片复制到应用私有存储目录,后续通过私有路径加载图片:
// 修改OnActivityResult代码,复制图片到私有目录 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == IMAGE_REQUEST_CODE && resultCode == RESULT_OK && data != null) { Uri selectedImageUri = data.getData(); if (selectedImageUri != null) { try { // 读取选中的图片流 InputStream inputStream = requireActivity().getContentResolver().openInputStream(selectedImageUri); // 创建应用私有目录下的文件 File privateDir = requireActivity().getFilesDir(); File imageFile = new File(privateDir, "user_profile.jpg"); // 将图片写入私有文件 FileOutputStream outputStream = new FileOutputStream(imageFile); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } inputStream.close(); outputStream.close(); // 保存私有路径到SharedPreferences SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("USERDATA", MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("imagePath", imageFile.getAbsolutePath()); editor.apply(); image_view.setImageURI(selectedImageUri); } catch (IOException e) { e.printStackTrace(); } } } } // 修改图片加载代码,通过私有路径加载 user_image = findViewById(R.id.user_image); String path = sharedPreferences.getString("imagePath", "default"); if (path.equals("default")) { user_image.setImageResource(R.drawable.profile); } else { // 推荐使用Glide/Picasso等图片加载库避免OOM // Glide.with(context).load(new File(path)).into(user_image); Bitmap bitmap = BitmapFactory.decodeFile(path); user_image.setImageBitmap(bitmap); }
注意事项
- 方法1无需占用额外存储,适合保留原图位置的场景
- 方法2需要处理文件IO异常,适合需要对图片编辑或二次处理的场景
- 禁止直接保存
ACTION_GET_CONTENT获取的URI并在后续组件中使用,临时权限不可靠
内容的提问来源于stack exchange,提问作者Syed Irfan Hussaini
相关产品推荐
相关产品推荐

