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

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访问整个外部存储的方式,步骤如下:

  1. 在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" />
  1. 动态申请特殊权限
    因为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

火山引擎 最新活动