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

Android 11三星平板获取外置SD卡路径读取文件失败如何解决

问题排查与修复方案

你遇到的访问失败是API认知错误、权限配置错误、实现方式过时共同导致的,逐个对应问题如下:

  • 你用的基础存储API本身就不返回SD卡路径:从Android 4.4版本开始,Environment.getExternalStorageDirectory()、Environment.getExternalStorageState()、Environment.getExternalStoragePublicDirectory()这一系列API的返回值固定指向设备内置共享存储,也就是你拿到的/storage/emulated/0路径,设计上就不会返回可移除SD卡的路径,和设备厂商、系统版本无关。
  • 你尝试的几种取SD卡路径的方式全部失效:
    • System.getenv("SECONDARY_STORAGE")是Android 4.3及更早版本使用的临时环境变量,Android 5.0之后系统已经不再写入这个值,返回的路径全是历史遗留的硬编码无效路径,和当前设备实际挂载点完全不匹配。
    • 你调用Environment.getExternalStoragePublicDirectory(Environment.MEDIA_MOUNTED)属于参数传错:MEDIA_MOUNTED是用来标识存储挂载状态的字符串常量,不是目录类型常量,传入这个参数等价于在内置存储下查找名为mounted的文件夹,返回的路径自然没有意义。
    • 硬编码/sdcard/external_sd这类路径没有任何兼容性:不同厂商、不同系统版本的SD卡挂载点命名规则完全不同,Android 5.0之后可移除SD卡普遍挂载在/storage/下以卷序列号命名的随机目录(类似/storage/1234-ABCD格式),硬编码固定路径100%会出现匹配失败。
  • 你的权限配置存在明显错误:
    • 你清单里声明的android.permission.PERMISSIONS_STORAGE、android.permission.REQUEST_EXTERNAL_STORAGE不是Android系统定义的合法权限,系统解析清单时会直接忽略这两个条目,没有任何实际作用。
    • Android 11系统上,READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE两个权限仅能访问内置共享存储内的媒体文件,无权访问可移除SD卡的文件系统,哪怕你写对了SD卡的真实路径,用File类直接访问也会被系统沙箱拦截,抛出文件不存在的异常,和文件实际是否存在无关。

正确实现方式

你只需要读取SD卡已存在的文件,不需要申请高风险的全文件访问权限,用Android官方提供的存储访问框架(SAF)实现即可,全机型兼容:

  1. 先清理清单文件,删除那两个无效的权限声明,不需要额外加特殊权限。
  2. 不要尝试通过环境变量、硬编码方式获取SD卡真实路径,也不要用File类直接访问SD卡路径,通过系统服务获取已挂载的可移除存储卷:
    val storageManager = getSystemService(Context.STORAGE_SERVICE) as StorageManager
    // 过滤出已正常挂载的可移除SD卡
    val mountedSdCard = storageManager.storageVolumes.firstOrNull {
        it.isRemovable && it.state == Environment.MEDIA_MOUNTED
    } ?: return // 没有检测到已挂载的SD卡
    
  3. 调用存储卷提供的方法拉起系统授权器,让用户选择你要访问的SD卡根目录,拿到系统授权的Uri后即可正常读取、遍历文件:
    // 拉起SD卡授权界面
    val authIntent = mountedSdCard.createOpenDocumentTreeIntent()
    startActivityForResult(authIntent, REQUEST_CODE_SD_CARD_AUTH)
    
  4. 在授权结果回调中拿到访问权限,后续通过DocumentFile类操作文件即可,不需要关心真实物理路径:
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE_SD_CARD_AUTH && resultCode == Activity.RESULT_OK) {
            val sdCardRootUri = data?.data ?: return
            // 持久化存储授权,下次应用启动不需要重复申请
            contentResolver.takePersistableUriPermission(
                sdCardRootUri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION
            )
            // 遍历/读取SD卡文件示例
            val sdCardRoot = DocumentFile.fromTreeUri(this, sdCardRootUri)
            sdCardRoot?.listFiles()?.forEach { doc ->
                Log.d("SD_CARD_FILE", "文件名:${doc.name}, 文件大小:${doc.length()}")
            }
        }
    }
    

补充说明:Android 10及以上版本对可移除存储做了强制沙箱隔离,第三方应用没有权限直接通过原生文件路径访问SD卡内容,必须通过系统返回的Uri、借助ContentResolver和DocumentFile操作,这也是其他可以正常访问SD卡的应用通用的实现方式。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:31:11