Android11 处理文件 出现 open failed: EACCES (Permission denied) 问题

针对Android10及以上版本引入的分区存储概念进行了深入探讨。详细分析了应用如何适配这一新特性,包括停用分区存储的方法及使用requestLegacyExternalStorage属性。此外,还介绍了在Android11中如何通过MANAGE_EXTERNAL_STORAGE权限实现对所有文件的访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题原因:

Android Q 引入了分区存储的概念
官方解释:以 Android 10(API 级别 29)及更高版本为目标平台的应用
默认情况下被赋予了对外部存储空间的分区访问权限(即分区存储)

分析:

分区存储改变了应用在设备的外部存储设备中存储和访问文件的方式。
虽然Android 为对外部存储空间的读写访问定义了以下权限:
READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。
但是对启用了分区存储方式的平台,访问外部存储也受到了限制。

解决:

Android 10(API 级别 29)为目标平台,请停用分区存储,继续使用适用于 Android 9 及更低版本的方法来执行此操作。
在清单文件 application节点内添加 requestLegacyExternalStorage属性

android:requestLegacyExternalStorage="true"

Android 11 (API 级别 30)为目标平台后,会强制执行分区存储,系统会忽略 requestLegacyExternalStorage 标记。并且WRITE_EXTERNAL_STORAGE 权限和 WRITE_MEDIA_STORAGE 特许权限将不再提供任何其他访问权限。
但是Android 提供了一种名为“所有文件访问权限”的特殊应用访问权限。在我们的清单文件内添加权限**<MANAGE_EXTERNAL_STORAGE>**
``

  <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
作用:

MANAGE_EXTERNAL_STORAGE权限授予以下访问:
1、对共享存储中的所有文件进行读写访问。
2、访问MediaStore.Files表的内容 。
3、访问USB便携式(OTG)驱动器和SD卡的根目录。
4、访问所有内部存储directories⁠,除了 /Android/data/,/sdcard/Android和大多数子目录 /sdcard/Android。

不过我们最好还是要迁移数据进行分区存储,以便更好地管理自己的文件、减少混乱。这样的话,即使在启用分区存储后,您的应用也可以继续访问这些文件。
需要注意的是从 Android 11 开始,
1、应用无法在外部存储设备上创建自己的应用专用目录。如需访问系统为您的应用提供的目录,请调用 getExternalFilesDirs()。
2、应用不能访问其他任何应用的数据目录中的文件。
3、应用无法再访问外部存储设备中的任何其他应用的专用于特定应用的目录中的文件。

详情请看:
Android 11 中的存储机制更新
Android 存储用例和最佳做法

<think>嗯,用户遇到了Android文件操作时的权限被拒绝问题,具体是EACCES (Permission denied)。我需要根据提供的引用资料来整理解决方案。首先,得确定问题的可能原因。根据引用[1]、[2]、[3]、[4],常见的原因包括没有正确申请权限、Android版本差异(比如Scoped Storage)、文件路径权限问题,以及厂商定制系统的问题。 第一步,应该检查是否在AndroidManifest.xml中添加了读写权限。引用[4]提到需要添加READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,可能还要MANAGE_EXTERNAL_STORAGE。但需要注意,从Android 10开始,作用域存储(Scoped Storage)限制了应用对外部存储的访问,所以可能需要使用MediaStore或者申请所有文件访问权限,这时候需要MANAGE_EXTERNAL_STORAGE,不过这个权限需要用户手动授权,而且可能被应用商店限制。 然后,动态申请权限也是关键。引用[3]中的代码示例展示了如何检查并请求权限,用户可能在安装时没有授予权限,或者只在旧版本Android处理了,但新版本需要运行时请求。所以必须确保在运行时请求这些权限,尤其是针对Android 6.0(API 23)及以上版本。 接下来,处理Android 10及以上版本的作用域存储问题。引用[2]提到添加android:requestLegacyExternalStorage="true"到应用清单,但这可能只在Android 10和11上有效,Android 12及以上可能需要其他方法。或者考虑使用应用专属目录,比如Context.getExternalFilesDir(),这样不需要申请存储权限,适用于存储应用私有文件。 另外,厂商定制系统如小米可能有额外的权限控制。用户可能需要手动在设置中授予应用存储权限,或者关闭MIUI优化,引用[2]提到这一点。这需要引导用户去设置里检查权限是否真正开启。 还有文件路径的问题,比如直接使用绝对路径如/storage/emulated/0/可能不可靠,应该使用Environment.getExternalStorageDirectory()或者更推荐的Context方法获取路径,避免硬编码路径带来的问题。 最后,如果所有方法都试过还是不行,可能需要考虑使用Storage Access Framework,通过Intent让用户选择文件或目录,这样系统会授予临时访问权限,引用中提到使用Intent.ACTION_OPEN_DOCUMENT或Intent.ACTION_CREATE_DOCUMENT。 现在需要将这些步骤结构化,分点说明,确保用户能逐步排查和解决问题。同时,生成的相关问题需要覆盖权限申请、不同Android版本的应对策略、厂商定制系统处理,以及替代方案如SAF。</think>### Android 文件打开失败 EACCES (Permission denied) 解决方案 #### 1. 检查并添加基础权限声明 - **步骤**:在 `AndroidManifest.xml` 中明确声明读写权限: ```xml <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ``` - **注意**:若需访问所有文件(不推荐),可声明 `MANAGE_EXTERNAL_STORAGE`,但需用户手动授权且可能违反应用商店政策[^4]。 #### 2. 动态申请运行时权限 - **代码示例**: ```java // 检查权限是否已授予 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // 弹出权限请求对话框 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE_PERMISSION); } ``` - **结果处理**:在 `onRequestPermissionsResult()` 中监听用户授权结果[^3]。 #### 3. 适配 Scoped Storage (Android 10+) - **方案1**:在 `AndroidManifest.xml` 中添加兼容标记(仅对 Android 10/11 有效): ```xml <application android:requestLegacyExternalStorage="true" ...> ``` - **方案2**:使用应用专属目录,无需权限: ```java File appDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS); ``` - **优势**:路径为 `/storage/emulated/0/Android/data/<包名>/`,可读写私有文件[^2]。 #### 4. 处理厂商定制系统权限 - **小米/红米手机**: 1. 进入 **设置 → 应用管理 → 权限 → 存储权限**,手动开启权限。 2. 尝试关闭 **开发者选项 → MIUI 优化**。 - **其他厂商**:类似路径检查权限是否被系统限制。 #### 5. 使用 SAF (Storage Access Framework) - **场景**:访问用户指定文件(如文档、图片): ```java Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("text/plain"); startActivityForResult(intent, REQUEST_CODE_OPEN_FILE); ``` - **结果**:通过 `onActivityResult()` 获取文件 URI 并操作。 #### 6. 验证文件路径合法性 - **避免硬编码路径**:使用系统 API 获取标准路径: ```java File publicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); ``` - **注意**:直接访问 `/storage/emulated/0/` 可能因系统限制失败[^1]。 ---
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值