You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开发文件管理器:Android13共享存储目录访问兼容方案咨询

解决方案:Android 13+ 兼容访问根目录与下载目录

问题核心

Android 13 对存储权限模型做了彻底调整:

  • 原有的 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限在 Android 13+ 不再触发弹窗,且无法用于访问非媒体类文件
  • 传统 File API 直接访问共享存储的方式在 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:

  1. 权限申请逻辑:
    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
            )
        }
    }
    
  2. 注意事项:
    • Google Play 审核时需明确说明应用核心功能依赖全存储访问,否则可能被拒
    • 国内应用市场审核相对宽松,但仍需符合平台规范

方案二:SAF + 内容解析器(无需特殊权限,需用户交互)

如果不想使用 MANAGE_EXTERNAL_STORAGE,可通过以下方式访问指定目录:

  1. 访问下载目录:
    直接通过 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))
                // 处理下载文件
            }
        }
    }
    
  2. 访问根目录:
    通过 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 权限 + File API
  • Android 13+:优先使用 MANAGE_EXTERNAL_STORAGE 权限(文件管理器场景),或结合 SAF + 内容解析器实现目录访问

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:13:21