Android 11下如何访问共享存储中应用清除数据/重装前创建的文件
Android 11 下绕过Scoped Storage后备份文件重装/清数据无法访问的解决方案
首先得明确问题根源:在Android 11(API 30)及以上,系统的Scoped Storage机制对传统File API的访问做了严格限制——哪怕你把文件放在公共Documents目录,应用在首次安装或清除数据后,默认也没有权限直接通过File API访问这些文件。之前能访问是因为应用创建文件时临时获得了关联权限,但清数据/重装后这个权限关联就彻底消失了。
结合你不想用Scoped Storage的需求,给你两个可行的解决方案:
方案一:申请MANAGE_EXTERNAL_STORAGE权限(绕过Scoped Storage限制)
这是目前唯一能让你继续用传统File API访问整个外部存储的方式,步骤如下:
- 在AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- 低版本兼容的读写权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="30" />
- 动态申请特殊权限:
因为MANAGE_EXTERNAL_STORAGE是系统特殊权限,不能通过普通的权限弹窗申请,需要引导用户到应用设置页面开启:
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); startActivity(intent); } } else { // 处理Android 10及以下的读写权限申请 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100); } }
注意:这个权限在Google Play审核时需要详细说明应用的使用场景(比如确实需要全存储访问的文件管理类功能),否则可能被拒,但相比Scoped Storage的复杂适配,这个方式更直接。
方案二:用MediaStore查询文件(兼容Scoped Storage的合规方案)
如果你不想走特殊权限的路子,也可以通过系统的MediaStore来查询公共目录下的文件,即使重装应用,只要文件存在就能被检索到。修改你的文件列表查询代码如下:
ArrayList<ObjJob> objFolderList = new ArrayList<>(); // 定义查询的字段和条件 String[] projection = { MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.DATA }; String selection = MediaStore.Files.FileColumns.DISPLAY_NAME + " LIKE ?"; String[] selectionArgs = new String[]{"%.jcbkp"}; String sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIED + " DESC"; Cursor cursor = getContentResolver().query( MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, sortOrder ); if (cursor != null) { while (cursor.moveToNext()) { String fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)); String filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)); // 过滤出你的目标目录下的文件 if (filePath.contains("/Documents/MTN Job Cards/Backups/")) { objFolderList.add(new ObjJob(fileName)); } } cursor.close(); }
额外提示:
- 检查你的代码中目录名称不一致的问题:创建文件时用的是
"Job Cards",查询时用的是"MTN Job Cards",这是明显的笔误,会导致找不到文件,先把这个统一! - 如果你选择方案一,申请权限后你的原文件创建和查询代码就能正常工作;如果选方案二,建议创建文件也改用MediaStore,确保文件能被系统正确索引。
内容的提问来源于stack exchange,提问作者DTD




