Android应用读取媒体文件崩溃:SecurityException权限问题排查
问题描述
已申请存储权限且日志显示权限已授予,但在RecyclerView中展示图片时,下拉返回或跳转至使用该图片的其他Activity时崩溃,报错信息:
java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/image%3A1000067180 from pid=29739, uid=10242 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
核心展示代码:
// onBindViewHolder: Binds data to the views in each ViewHolder @Override public void onBindViewHolder(@NonNull PostsListAdapter.PostViewHolder holder, int position) { if (posts != null) { final Post current = posts.get(position); holder.tvAuthor.setText(current.getAuthor()); holder.tvcontent.setText(current.getContent()); holder.ivPic.setImageURI(current.getPic()); holder.ivProfile.setImageURI(current.getProfile()); holder.tvLikes.setText(String.valueOf(current.getLikes())); } }
问题根源
普通的READ_EXTERNAL_STORAGE权限无法访问MediaDocumentsProvider提供的URI——这类URI是系统文件选择器生成的,属于文档级别的独立访问权限,和全局存储权限是两个概念。即使申请了存储权限,应用切换页面、重启后,临时的URI访问权限会自动失效,触发读取时的权限拒绝。
解决方案
1. 改用ACTION_OPEN_DOCUMENT获取图片URI
放弃直接通过文件路径或普通权限访问的方式,使用系统提供的ACTION_OPEN_DOCUMENT(推荐)选择图片,这类API会自动申请临时访问权限:
// AndroidX推荐的ActivityResult方式 registerForActivityResult(new ActivityResultContracts.OpenDocument(), uri -> { if (uri != null) { // 持久化URI权限,避免页面切换或重启后失效 int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION; getContentResolver().takePersistableUriPermission(uri, takeFlags); // 将持久化后的URI保存到Post对象 Post newPost = new Post(...); newPost.setPic(uri); posts.add(newPost); adapter.notifyDataSetChanged(); } }).launch(new String[]{"image/*"});
2. 强制持久化URI权限
获取URI后必须调用takePersistableUriPermission,让系统保留该URI的长期访问权限,这是解决页面切换后权限失效的核心步骤。
3. 验证URI权限有效性(可选)
如果需要提前判断URI是否还能访问,可添加权限检查逻辑:
public boolean hasUriPermission(Context context, Uri uri) { int permission = context.getContentResolver().checkUriPermission(uri, android.os.Process.myPid(), android.os.Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION); return permission == PackageManager.PERMISSION_GRANTED; }
若权限失效,需重新引导用户选择图片。
内容的提问来源于stack exchange,提问作者yael dahari

