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

Android 11如何获取目录文件列表?开发轻量文件浏览器遇Scoped Storage难题

在Android 11通过Scoped Storage访问/sdcard目录的方案

嘿,刚好我之前做过类似的自定义轻量文件浏览器,来给你说说Android 11下用Scoped Storage访问/sdcard(外部存储根目录)的正确姿势——毕竟Scoped Storage把直接硬编码访问根目录的路子给堵死了,得换个思路来搞。

核心逻辑:Scoped Storage的权限限制

Android 11及以上版本,Scoped Storage默认只允许你的应用访问自身私有目录和媒体文件(图/音/视频)。如果要访问/sdcard根目录下的非媒体文件(比如文档、压缩包),有两种合法途径:


方法一:使用存储访问框架(SAF)让用户授权(推荐)

这是Google推荐的方式,符合Scoped Storage的设计理念,不需要申请特殊权限,只需让用户手动选择/sdcard目录并授权。

步骤1:启动文档树选择器

用ACTION_OPEN_DOCUMENT_TREEIntent唤起系统的目录选择器,让用户选择/sdcard根目录。注意从API 30开始,startActivityForResult被弃用,推荐用registerForActivityResult:

// 初始化ActivityResultLauncher,处理用户选择后的回调
private val pickRootDirLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { selectedUri ->
    selectedUri?.let { uri ->
        // 步骤2:申请持久访问权限,避免重启应用后失去权限
        val permissionFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        contentResolver.takePersistableUriPermission(uri, permissionFlags)
        
        // 步骤3:用DocumentFile操作目录内容
        val rootDir = DocumentFile.fromTreeUri(this, uri)
        rootDir?.listFiles()?.forEach { file ->
            // 遍历根目录下的文件/子目录
            val type = if (file.isDirectory) "目录" else "文件"
            println("${file.name} - $type")
        }
    }
}

// 触发目录选择的方法
fun openSdcardRoot() {
    // 可选:默认跳转到/sdcard目录(Android 11+支持)
    val initialSdcardUri = MediaStore.getExternalStorageVolume().storageUri
    pickRootDirLauncher.launch(initialSdcardUri)
}

关键说明:

  • DocumentFile是SAF的核心操作类,替代了传统的File类,用来遍历、创建、删除文件/目录。
  • 持久权限takePersistableUriPermission必须调用,否则应用重启后之前授权的Uri会失效。

方法二:申请MANAGE_EXTERNAL_STORAGE特殊权限(谨慎使用)

如果你的应用是正经的文件管理器、备份工具等符合Google规定的场景,可以申请这个权限来直接访问整个外部存储。但要注意:

  • 这个权限是特殊权限,用户需要手动去应用设置里开启。
  • Google Play对这个权限的审核非常严格,非合规应用会被拒审。

步骤1:在Manifest中声明权限

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

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

fun checkAndRequestFullStoragePermission() {
    if (!Environment.isExternalStorageManager()) {
        // 跳转到应用的所有文件访问权限设置页
        val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
        intent.data = Uri.parse("package:$packageName")
        startActivity(intent)
    } else {
        // 权限已开启,可以用传统File API访问/sdcard了
        val sdcardRoot = Environment.getExternalStorageDirectory()
        sdcardRoot.listFiles()?.forEach { file ->
            println("${file.name} - ${if (file.isDirectory) "目录" else "文件"}")
        }
    }
}

最后提醒

  • 优先用SAF的方式,因为它更符合Android的安全规范,也不会有审核风险。
  • 不要尝试绕过Scoped Storage的限制(比如通过root或者私有API),这会导致应用兼容性问题,甚至被Google Play下架。

内容的提问来源于stack exchange,提问作者Алексей Мальченко

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 06:17:42