Android 10无法访问应用外部文件的技术求助
你遇到的问题核心是Android 10(API 29)引入的分区存储(Scoped Storage)机制——即便你配置了READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,默认情况下也无法直接通过Environment.getExternalStoragePublicDirectory()访问公共目录的文件。下面分两种方案帮你解决:
一、临时过渡方案(快速解决)
如果想继续沿用旧的文件访问逻辑,可以在AndroidManifest.xml的application标签中添加android:requestLegacyExternalStorage="true",强制禁用分区存储:
<application android:allowBackup="true" android:largeHeap="true" android:fullBackupContent="@xml/backup_descriptor" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:requestLegacyExternalStorage="true"> <!-- 添加这一行 --> <!-- 其他组件配置 --> </application>
这个配置会让应用在Android 10上使用传统存储模式,你之前的Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)就能正常获取文件列表了。注意这是过渡方案,在Android 11及以上版本需要额外适配。
二、长期适配方案(符合新存储规范)
如果要适配Android 10+的分区存储,推荐使用以下两种方式:
1. 使用MediaStore查询公共目录文件
对于下载目录的文件,可以通过MediaStore.Downloads来查询,示例代码:
ContentResolver resolver = context.getContentResolver(); Uri uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI; String[] projection = {MediaStore.Downloads._ID, MediaStore.Downloads.DISPLAY_NAME}; Cursor cursor = resolver.query(uri, projection, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { String fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Downloads.DISPLAY_NAME)); // 处理文件名或获取文件路径 } cursor.close(); }
2. 使用存储访问框架(SAF)让用户选择文件/目录
如果需要让用户手动选择下载目录的文件,可以启动系统文件选择器:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); // 允许选择所有类型文件 ((Activity) context).startActivityForResult(intent, REQUEST_CODE_PICK_FILE);
然后在onActivityResult(或AndroidX的registerForActivityResult)中获取选中文件的Uri,进行后续操作。
额外优化:权限申请代码调整
你的权限申请逻辑里包含了FOREGROUND_SERVICE权限,这个权限在API 29中属于正常权限,不需要动态申请,所以可以简化判断逻辑:
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_FOLDER_CONF); }
最后补充:你能正常访问应用内的getExternalFilesDir()目录,是因为这个目录属于应用专属存储区域,分区存储机制不会限制应用对自身专属目录的访问权限。
内容的提问来源于stack exchange,提问作者koskokos_231




