安卓部分设备申请权限后仍出现java.io.IOException: Permission denied问题
解决Android中
imageFile.createNewFile()抛出Permission Denied异常的问题 你已经做了基础的权限声明和动态申请,但还是有部分设备出现权限拒绝的问题,我来帮你梳理几个核心原因和对应的解决方案:
1. Android 10+ 分区存储(Scoped Storage)的限制
从API 29开始,Android引入了分区存储机制——即使你申请了WRITE_EXTERNAL_STORAGE权限,直接通过File类操作公共下载目录(DIRECTORY_DOWNLOADS)也会被系统限制,这是新版本设备报错的最常见原因。
解决方案:
- 方案一:使用MediaStore API保存文件(推荐)
对于API 29+,建议通过MediaStore将文件写入公共下载目录,示例代码如下:// 适配API29+的文件保存逻辑 private void saveFileToDownloads(String fileName, byte[] fileData) throws IOException { ContentValues values = new ContentValues(); values.put(MediaStore.Downloads.DISPLAY_NAME, fileName); values.put(MediaStore.Downloads.MIME_TYPE, "image/jpeg"); // 根据你的文件类型调整 values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); ContentResolver resolver = getContext().getContentResolver(); Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values); if (uri == null) { throw new IOException("Failed to create new MediaStore record."); } try (OutputStream os = resolver.openOutputStream(uri)) { os.write(fileData); } finally { resolver.update(uri, values, null, null); } } - 方案二:兼容旧存储模型(仅API29-30有效)
如果暂时不想适配MediaStore,可以在AndroidManifest.xml的<application>标签中添加:
注意:这个属性在API31及以上版本会被忽略,长远来看还是建议适配MediaStore。android:requestLegacyExternalStorage="true"
2. 权限被用户动态撤销
有时候用户会在App运行过程中,手动去系统设置里撤销存储权限,这时候EasyPermissions.hasPermissions()可能因为缓存返回true,但实际操作文件时权限已经失效。
解决方案:
在文件操作的try-catch块中捕获IOException,当检测到权限拒绝时,重新检查权限并引导用户开启:
try { imageFile.createNewFile(); } catch (IOException e) { if (e.getMessage().contains("Permission denied")) { // 重新检查权限 if (!EasyPermissions.hasPermissions(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) { EasyPermissions.requestPermissions(this, getString(R.string.please_grant_permission), RC_SAVE, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}); } else { // 权限存在但仍报错,可能是系统限制,引导用户去设置页开启 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getContext().getPackageName(), null); intent.setData(uri); startActivity(intent); } } else { // 处理其他IO异常 e.printStackTrace(); } }
3. 目录创建逻辑的隐患
你的代码中使用downloadsFolder.mkdir()来创建目录,但这个方法只能创建一级目录,如果父目录不存在会返回false,导致后续创建文件失败,容易被误以为是权限问题。
解决方案:
将mkdir()替换为mkdirs(),并检查目录是否创建成功:
File downloadsFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); if (!downloadsFolder.exists()) { // mkdirs()会创建所有不存在的父目录 boolean isCreated = downloadsFolder.mkdirs(); if (!isCreated) { Log.e("Storage", "Failed to create downloads folder"); return; // 目录创建失败,终止后续操作 } }
4. 定制ROM的特殊权限限制
部分国产定制ROM(如小米、华为等)对存储权限做了额外细分,比如API30+之后,除了WRITE_EXTERNAL_STORAGE,还需要申请MANAGE_EXTERNAL_STORAGE权限才能访问所有文件——这个权限无法通过动态申请弹窗获取,需要用户手动去设置开启。
解决方案:
- 对于API30+的设备,引导用户开启“所有文件访问权限”:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getContext().getPackageName(), null); intent.setData(uri); startActivity(intent); } }
- 同时在
AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
5. 多进程场景下的权限同步问题
如果你的App使用了多进程,动态申请的权限可能不会同步到非主进程,导致非主进程操作文件时出现权限拒绝。
解决方案:
- 尽量将文件操作逻辑放在主进程中执行;
- 如果必须在非主进程操作,确保每个进程都做权限检查,或者通过主进程的ContentProvider来处理文件操作。
内容的提问来源于stack exchange,提问作者Yevhenii
相关产品推荐
相关产品推荐

