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
相关产品推荐
相关产品推荐

