Android 11及以上版本从相册选图片提示权限被拒问题求助
Android 11+ 相册选图权限拒绝问题解决指南
问题核心
已在Manifest配置对应权限,但Android 11及以上版本从相册选图时仍提示"access denied",目标是完成选图、裁剪并转换为Bitmap。
权限配置确认(当前配置没问题,无需修改)
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
代码问题分析与修复
1. 替换Uri.fromFile()为FileProvider(Android 7+跨应用Uri访问限制)
Android 7.0开始禁止跨应用传递file://格式Uri,会触发权限拒绝或异常,必须用FileProvider生成content://格式Uri。
步骤1:配置FileProvider
在AndroidManifest.xml的<application>标签内添加:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
在res/xml目录下创建file_paths.xml:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-cache-path name="external_cache" path="." /> </paths>
步骤2:修改选图代码(移除多余的EXTRA_OUTPUT)
ACTION_PICK无需设置输出Uri,选图结果直接从data.getData()返回:
public void selectImageFromGallery(ImageView view) { setResourceImg(view); checkListener(); Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.setType("image/*"); if (fragment == null) { mContext.startActivityForResult(intent, REQUEST_PICTURE_FROM_GALLERY); } else { fragment.startActivityForResult(intent, REQUEST_PICTURE_FROM_GALLERY); } }
步骤3:修改裁剪代码(使用FileProvider并授予临时权限)
给裁剪应用授予读写Uri的临时权限,避免权限拒绝:
public void requestCropImage(Uri uri, int outputX, int outputY, int aspectX, int aspectY) { checkListener(); if (tempFileFromCrop == null) { try { tempFileFromCrop = File.createTempFile("crop", "png", mContext.getExternalCacheDir()); } catch (IOException e) { e.printStackTrace(); return; } } // 用FileProvider生成content://格式Uri tempUriFromCrop = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".fileprovider", tempFileFromCrop); final Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); // 授予裁剪应用读写权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.putExtra("output", tempUriFromCrop); intent.putExtra("outputX", outputX); intent.putExtra("outputY", outputY); intent.putExtra("aspectX", aspectX); intent.putExtra("aspectY", aspectY); intent.putExtra("scale", true); intent.putExtra("noFaceDetection", true); // 确保有应用能处理裁剪Intent if (intent.resolveActivity(mContext.getPackageManager()) != null) { if (fragment == null) { mContext.startActivityForResult(intent, REQUEST_CROP_PICTURE); } else { fragment.startActivityForResult(intent, REQUEST_CROP_PICTURE); } } }
2. 修正Bitmap获取方式(Android 11+路径访问限制)
Android 11+无法通过MediaStore.Images.Media.DATA字段获取的路径直接访问文件,需通过ContentResolver打开输入流:
@Override public void onImageCropped(Uri uri, File imageFile) { try { InputStream inputStream = getContext().getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); // 此处处理bitmap if (inputStream != null) { inputStream.close(); } } catch (Exception e) { e.printStackTrace(); } }
3. 补充动态权限申请
Android 13+需动态申请READ_MEDIA_IMAGES,Android 10-12需动态申请READ_EXTERNAL_STORAGE,确保选图前已获取权限:
// 示例:用ActivityResultLauncher申请权限 private ActivityResultLauncher<String> permissionLauncher = registerForActivityResult( new ActivityResultContracts.RequestPermission(), isGranted -> { if (isGranted) { // 权限已获取,执行选图逻辑 } else { // 权限被拒绝,提示用户开启 } } ); private void checkAndRequestPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) { permissionLauncher.launch(Manifest.permission.READ_MEDIA_IMAGES); } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); } } else { if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); } } }
内容的提问来源于stack exchange,提问作者Mohammad
相关产品推荐
相关产品推荐

