Android 11 无法读取指定应用Android/data目录文件的解决方案求助
Android 11+ 访问其他应用Android/data目录失效解决方案
问题原因
Android 11(API 30)开始系统强制启用*分区存储(Scoped Storage)*机制,第三方应用默认被禁止直接访问其他应用的Android/data/、Android/obb/私有目录,因此你原来的直接拼接路径读取文件的逻辑会失效。
可行解决方案
场景1:目标文件所属的应用也是你开发的
这种优先用官方推荐的跨应用共享方案,无需申请特殊权限,兼容性最好:
- 方案1:通过ContentProvider共享文件
在提供录音文件的应用中注册ContentProvider,暴露对应Recordings目录下的文件资源,你的访问方应用通过ContentResolver即可查询、读取对应的文件,是跨应用共享数据的标准实现。 - 方案2:同签名应用配置sharedUserId
如果两个应用使用同一套签名打包,可以在两个应用的AndroidManifest.xml的<manifest>标签中配置相同的android:sharedUserId值,配置后两个应用会运行在同一个沙箱环境,可以直接互相访问私有目录。注意:如果应用已经上线,修改sharedUserId会导致老用户无法覆盖安装,需谨慎使用。
场景2:目标文件所属的应用不是你开发的,无法修改对方代码
方案A:申请全量存储管理权限(MANAGE_EXTERNAL_STORAGE)
该权限属于特殊权限,授权后可以访问外部存储的所有目录,包括其他应用的私有目录。
- 首先在
AndroidManifest.xml中声明权限:
<!-- 兼容低版本存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 11及以上需要的全量存储权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- 运行时引导用户授权:
// 权限申请请求码 private static final int REQUEST_MANAGE_STORAGE = 1001; private static final int REQUEST_READ_STORAGE = 1002; private void checkPermissionAndLoadFiles() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Environment.isExternalStorageManager()) { // 已获得权限,直接读取文件 loadRecordingFiles(); } else { // 跳转到系统设置页引导用户授权 Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_MANAGE_STORAGE); } } else { // 低版本申请普通存储权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { loadRecordingFiles(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_READ_STORAGE); } } } // 你原有逻辑封装成该方法即可 private void loadRecordingFiles() { String path = Environment.getExternalStorageDirectory().toString() + "/Android/data/.../files/Recordings"; File directory = new File(path); File[] files = directory.listFiles(); if (directory.canRead() && files != null) { showRecFiles(files); } else { Toast.makeText(this, "Empty!!!", Toast.LENGTH_SHORT).show(); } } // 权限申请回调处理 @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_MANAGE_STORAGE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Environment.isExternalStorageManager()) { loadRecordingFiles(); } else { Toast.makeText(this, "未获得存储权限,无法读取文件", Toast.LENGTH_SHORT).show(); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_READ_STORAGE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { loadRecordingFiles(); } else { Toast.makeText(this, "未获得存储权限,无法读取文件", Toast.LENGTH_SHORT).show(); } } }
注意:如果你的应用需要上架谷歌Play商店,该权限仅允许文件管理器、备份、杀毒等必须访问全量存储的应用申请,不符合使用场景的应用会被拒审或下架。
方案B:使用存储访问框架(SAF)引导用户主动授权目录
无需申请特殊权限,也符合谷歌应用商店规则,通过系统的文件选择器引导用户手动选中目标Recordings目录,授权后即可读写该目录下的所有内容,缺点是需要用户手动操作选择目录。
内容的提问来源于stack exchange,提问作者Ervin
相关产品推荐
相关产品推荐

