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

Android 11设备获取系统Download目录文件列表失败问题求助

解决Android 11获取系统公共Download目录文件的问题

嘿,我一眼就看出问题所在啦!你当前代码里用的getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),返回的是你的应用专属的私有Download子目录(路径大概是/storage/emulated/0/Android/data/[你的应用包名]/files/Download),而不是你在文件管理器里看到的那个系统公共Download目录(/storage/emulated/0/Download)——这就是为啥代码显示没文件,但实际目录里有内容的原因!

针对Android 11(API 30)的存储权限变更,我给你几个可行的解决方案:

方案1:通过MediaStore查询公共Download目录

Android 11推荐用MediaStore来访问公共存储里的文件,这种方式不需要申请特殊权限(非媒体文件除外,后面会说明):

代码示例

// 定义要查询的公共Download目录Uri
Uri downloadUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI;

// 指定要获取的文件字段
String[] projection = {
    MediaStore.Downloads._ID,
    MediaStore.Downloads.DISPLAY_NAME,
    MediaStore.Downloads.SIZE
};

// 查询条件(留空可获取所有文件)
String selection = null;
String[] selectionArgs = null;
// 按文件添加时间倒序排列
String sortOrder = MediaStore.Downloads.DATE_ADDED + " DESC";

// 执行查询
Cursor cursor = getContentResolver().query(
    downloadUri,
    projection,
    selection,
    selectionArgs,
    sortOrder
);

if (cursor != null) {
    while (cursor.moveToNext()) {
        String fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Downloads.DISPLAY_NAME));
        long fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads.SIZE));
        Log.i("Files", "文件名:" + fileName + ",大小:" + fileSize + "字节");
    }
    cursor.close();
}

注意事项

  • 如果要访问非媒体类型的文件(比如文档、压缩包),Android 11需要申请MANAGE_EXTERNAL_STORAGE权限,这个权限是特殊权限,需要引导用户到系统设置开启(具体代码见方案3)。
  • 不要直接用MediaStore.Downloads.DATA字段获取文件路径,Android 10及以上已经限制了直接访问路径,推荐用getContentResolver().openInputStream(downloadUri.buildUpon().appendPath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID))).build())来获取文件输入流。

方案2:使用存储访问框架(SAF)让用户授权访问

这种方式完全符合Android隐私政策,不需要申请敏感权限,让用户手动选择Download目录并授权:

代码示例

  1. 启动SAF选择目录:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
// 默认定位到Download目录
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, 1001);
  1. 处理用户授权后的回调:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1001 && resultCode == RESULT_OK) {
        Uri treeUri = data.getData();
        // 持久化权限,避免下次打开应用重新授权
        getContentResolver().takePersistableUriPermission(treeUri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION);
        
        // 遍历目录下的文件
        DocumentFile rootDir = DocumentFile.fromTreeUri(this, treeUri);
        if (rootDir != null && rootDir.isDirectory()) {
            for (DocumentFile file : rootDir.listFiles()) {
                Log.i("Files", "文件名:" + file.getName());
            }
        }
    }
}

如果用Jetpack的ActivityResultContracts,代码会更简洁,你可以根据自己的项目架构调整。

方案3:申请MANAGE_EXTERNAL_STORAGE权限(不推荐,仅特殊场景使用)

如果你的应用必须直接访问整个公共存储,才考虑用这个方案——因为Google对这个权限的审核很严格,只有文件管理器、备份类等特殊应用才容易通过审核。

步骤1:在Manifest中添加权限

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    android:minSdkVersion="30" />

步骤2:检查并引导用户开启权限

if (Environment.isExternalStorageManager()) {
    // 权限已授予,可以直接访问公共Download目录
    File publicDownloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
    File[] files = publicDownloadDir.listFiles();
    if (files != null) {
        for (File file : files) {
            Log.i("Files", file.getName());
        }
    }
} else {
    // 引导用户到系统设置开启权限
    Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
    Uri uri = Uri.fromParts("package", getPackageName(), null);
    intent.setData(uri);
    startActivity(intent);
}

最后再提醒一句:优先选择方案1或2,尽量避免方案3,这样你的应用不仅符合Android的隐私规范,也更容易通过Google Play的审核~

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

火山引擎 最新活动