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目录并授权:
代码示例
- 启动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);
- 处理用户授权后的回调:
@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




