You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android读取文本文件遇EACCES权限拒绝错误,求解决方案

解决Android外部存储文件读取权限拒绝(EACCES)问题

你遇到的这个权限拒绝问题,大多是Android 10及以上版本的**分区存储(Scoped Storage)**机制导致的——哪怕你已经在Manifest声明了权限、也申请了运行时权限,系统依然会限制对外部存储任意目录的直接访问。下面是针对性的解决方案:

1. 适配Android 10(API 29)的临时兼容方案

Android 10默认启用分区存储,若想继续用传统的文件路径访问方式,需要在AndroidManifest.xml<application>标签中添加兼容属性:

<application
    android:name=".MyApplication"
    ...
    android:requestLegacyExternalStorage="true">
    ...
</application>

这个属性会让应用以Android 9及以下的存储逻辑运行,绕过分区存储限制,但这只是临时过渡方案,Android 11及以上会有更严格的限制。

2. 必须处理权限请求的回调逻辑

你只写了权限检查和请求的代码,但缺少权限申请结果的回调处理。必须在Activity中重写onRequestPermissionsResult方法,确认权限被授予后再执行文件读取:

private static final int STORAGE_PERMISSION_REQUEST = 1001;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == STORAGE_PERMISSION_REQUEST) {
        // 检查读写权限是否全部授予
        boolean allGranted = true;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }
        if (allGranted) {
            // 权限通过,执行文件读取逻辑
            readXConfigFile();
        } else {
            Toast.makeText(this, "存储权限被拒绝,无法读取文件", Toast.LENGTH_SHORT).show();
        }
    }
}

⚠️ 注意:不要在调用requestPermissions后立即执行文件读取,必须等待回调确认权限已授予。

3. Android 11(API 30)及以上的特殊处理

Android 11开始,requestLegacyExternalStorage不再生效。如果需要访问外部存储的任意目录,你需要申请MANAGE_EXTERNAL_STORAGE权限:

步骤1:在Manifest中添加权限

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

步骤2:引导用户手动开启权限

这个权限无法通过普通运行时请求获取,需要跳转到系统设置页面让用户手动开启:

private static final int MANAGE_STORAGE_REQUEST = 1002;

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", getPackageName(), null);
        intent.setData(uri);
        startActivityForResult(intent, MANAGE_STORAGE_REQUEST);
    } else {
        // 已获得权限,执行文件读取
        readXConfigFile();
    }
}

然后在onActivityResult中确认权限状态:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == MANAGE_STORAGE_REQUEST) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            if (Environment.isExternalStorageManager()) {
                readXConfigFile();
            } else {
                Toast.makeText(this, "需要授予全存储访问权限才能读取文件", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

4. 提前确认目录是否存在

在创建File对象前,先确保sos目录已存在,避免因目录不存在导致的间接权限报错:

File sdcard = Environment.getExternalStorageDirectory();
File sosDir = new File(sdcard, "sos");
// 创建多级目录(如果不存在)
if (!sosDir.exists()) {
    boolean dirCreated = sosDir.mkdirs();
    if (!dirCreated) {
        Toast.makeText(this, "无法创建sos目录", Toast.LENGTH_SHORT).show();
        return;
    }
}
File file = new File(sosDir, "xconfig.txt");

长期适配建议

以上都是兼容传统存储方式的临时方案,Google推荐从Android 10开始适配分区存储存储访问框架(SAF)

  • 如果是媒体文件,使用MediaStoreAPI访问
  • 如果是非媒体文件,使用SAF让用户主动选择文件/目录,无需依赖特殊权限

内容的提问来源于stack exchange,提问作者Piyush Chandra

火山引擎 最新活动