You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中READ_EXTERNAL_STORAGE权限对XML文件失效问题排查

解决已授予READ_EXTERNAL_STORAGE仍无法读取XML文件的问题

你的问题核心是Android 10(API 29)及以上的分区存储机制导致的:

  • MediaPlayer能读取同目录MP3,是因为它通过MediaStore访问媒体文件,不受直接File访问的限制;
  • 但你用File类直接访问公共Documents目录下的文件,即使有READ_EXTERNAL_STORAGE权限,在分区存储规则下也会被拒绝。

具体解决方案

方案1:通过ContentResolver + MediaStore获取文件输入流(推荐适配新系统)

放弃直接用File对象,改为通过MediaStore查询文件的Uri,再用ContentResolver打开流来解析XML:

private Document loadSongDictionary() throws IOException, SAXException, ParserConfigurationException {
    // 查询Documents目录下的目标XML文件
    Uri contentUri = MediaStore.Files.getContentUri("external");
    String[] projection = {MediaStore.Files.FileColumns._ID};
    String selection = MediaStore.Files.FileColumns.DISPLAY_NAME + " = ? AND "
            + MediaStore.Files.FileColumns.RELATIVE_PATH + " = ?";
    String[] selectionArgs = {"Dictionary.xml", "Documents/SkyePlayerData/"};

    Cursor cursor = getContentResolver().query(contentUri, projection, selection, selectionArgs, null);
    if (cursor == null || cursor.getCount() == 0) {
        cursor.close();
        return null;
    }

    cursor.moveToFirst();
    long fileId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID));
    Uri fileUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), fileId);

    // 通过ContentResolver打开输入流
    try (InputStream inputStream = getContentResolver().openInputStream(fileUri)) {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(inputStream);
        doc.getDocumentElement().normalize();
        cursor.close();
        return doc;
    }
}

方案2:将文件迁移到APP专属存储(更简单,无需权限)

如果这个XML文件是你的APP创建的,建议把它放到APP的专属外部存储目录,不需要任何权限就能读写:

// 获取APP专属外部存储的Documents目录
File appDocsDir = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "SkyePlayerData");
if (!appDocsDir.exists()) {
    appDocsDir.mkdirs();
}
File inputFile = new File(appDocsDir, "Dictionary.xml");
// 后续解析代码不变

方案3:兼容Android 10的临时方案(仅API 29有效)

如果你的APP只需要适配到Android 10,可以在AndroidManifest.xml的<application>标签里添加:

android:requestLegacyExternalStorage="true"

这个属性会让APP暂时绕过分区存储限制,但Android 11及以上版本无效。

额外提示

你的getPermissionToReadFile方法有个小问题:即使权限申请还没完成,也直接返回true,后续代码会继续执行。正确的做法应该是在权限申请回调onRequestPermissionsResult里再触发文件读取操作,但这个不是你当前权限拒绝的原因,只是代码优化点。

内容的提问来源于stack exchange,提问作者David Orr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 18:55:12