开发文件管理器:Android13共享存储目录访问兼容方案咨询
解决方案:Android 13+ 兼容访问根目录与下载目录
问题核心
Android 13 对存储权限模型做了彻底调整:
- 原有的
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限在 Android 13+ 不再触发弹窗,且无法用于访问非媒体类文件 - 传统
FileAPI 直接访问共享存储的方式在 Android 13+ 受到严格限制
分版本权限配置
在 AndroidManifest.xml 中按系统版本配置对应权限:
<!-- Android 12 及以下版本 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="31" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="31" /> <!-- Android 10/11 兼容:启用旧存储模型 --> <application ... android:requestLegacyExternalStorage="true"> </application> <!-- Android 13+ 媒体权限(如果需要访问图片/视频/音频) --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- Android 13+ 全存储访问权限(文件管理器核心功能必备) --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />
访问根目录与下载目录的具体实现
方案一:MANAGE_EXTERNAL_STORAGE 权限(推荐文件管理器使用)
该权限允许应用访问所有共享存储目录,包括根目录和下载目录,且兼容 File API:
- 权限申请逻辑:
private const val PERMISSION_REQUEST_CODE = 1000 fun checkAndRequestAllFilesPermission(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 判断是否已获取全存储权限 if (!Environment.isExternalStorageManager()) { // 跳转到系统设置页面让用户授权 val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:${context.packageName}") context.startActivity(intent) } else { // 已授权,可直接用 File API 访问根目录/下载目录 val rootFiles = File(Environment.getExternalStorageDirectory().path).listFiles() val downloadFiles = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path).listFiles() // 处理文件列表 } } else { // Android 12 及以下,请求传统读写权限 ActivityCompat.requestPermissions( context as Activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE ) } } - 注意事项:
- Google Play 审核时需明确说明应用核心功能依赖全存储访问,否则可能被拒
- 国内应用市场审核相对宽松,但仍需符合平台规范
方案二:SAF + 内容解析器(无需特殊权限,需用户交互)
如果不想使用 MANAGE_EXTERNAL_STORAGE,可通过以下方式访问指定目录:
- 访问下载目录:
直接通过MediaStore.Downloads内容 URI 查询,无需用户手动选择目录:fun getDownloadFiles(context: Context) { val projection = arrayOf( MediaStore.Downloads._ID, MediaStore.Downloads.DISPLAY_NAME, MediaStore.Downloads.RELATIVE_PATH ) val cursor = context.contentResolver.query( MediaStore.Downloads.EXTERNAL_CONTENT_URI, projection, null, null, "${MediaStore.Downloads.DATE_MODIFIED} DESC" ) cursor?.use { while (it.moveToNext()) { val fileName = it.getString(it.getColumnIndexOrThrow(MediaStore.Downloads.DISPLAY_NAME)) val filePath = it.getString(it.getColumnIndexOrThrow(MediaStore.Downloads.RELATIVE_PATH)) // 处理下载文件 } } } - 访问根目录:
通过 SAF 让用户选择根目录,保存持久化权限后可重复访问:private val SAF_ROOT_REQUEST_CODE = 1001 fun requestRootDirectoryAccess(activity: Activity) { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) activity.startActivityForResult(intent, SAF_ROOT_REQUEST_CODE) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == SAF_ROOT_REQUEST_CODE && resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> // 保存持久化权限 contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) // 通过 DocumentFile 访问根目录文件 val rootDir = DocumentFile.fromTreeUri(this, uri) rootDir?.listFiles()?.forEach { file -> // 处理文件/目录 } } } }
兼容性总结
- Android 12 及以下:使用传统
READ/WRITE_EXTERNAL_STORAGE权限 +FileAPI - Android 13+:优先使用
MANAGE_EXTERNAL_STORAGE权限(文件管理器场景),或结合 SAF + 内容解析器实现目录访问
内容的提问来源于stack exchange,提问作者CoolDoge
相关产品推荐
相关产品推荐

