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

Kotlin读取安卓SD卡文件夹音频文件,getExternalStorageDirectory废弃后实现求助

原因分析

  • 硬编码/sdcard/路径不兼容:不同安卓设备的外置存储挂载路径存在差异,直接写死路径大概率会出现目录找不到的问题
  • 权限未真正生效:安卓6.0及以上版本仅在Manifest中声明存储权限无效,需要主动触发动态权限申请,用户同意后才能获得读取权限;安卓10及以上版本默认开启分区存储,普通应用无法直接访问外置存储根目录的公共文件夹
  • getExternalFilesDir()使用认知偏差:该API获取的是应用专属的外部存储目录(对应路径为外部存储/Android/data/你的应用包名/files/),仅你的应用可访问,不需要权限,适合存放应用私有文件,和原来getExternalStorageDirectory()获取的公共存储根目录定位不同

解决方法

方案1:使用应用专属目录(推荐,无需权限)

如果这些音频文件仅你的应用使用,直接用getExternalFilesDir()创建专属目录存放即可,代码示例:

fun getaudio() {
    // 传入"Audio"会自动在应用专属目录下创建Audio子文件夹
    val appAudioDir = getExternalFilesDir("Audio")
    if (appAudioDir != null && appAudioDir.exists() && appAudioDir.isDirectory) {
        val files = appAudioDir.listFiles()
        Log.d("mytag", "No of files is ${files?.size ?: 0}")
    }
}

方案2:访问公共存储的Audiobooks目录

如果需要读取其他应用存入公共目录的音频,按系统版本适配:

  1. Manifest配置
    <!-- 安卓9及以下需要读存储权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!-- 安卓10临时绕过分区存储限制,安卓11及以上不生效 -->
    <application
        android:requestLegacyExternalStorage="true"
        ...>
    </application>
    
  2. 动态申请权限
    页面启动时主动申请读取存储权限,用户同意后再执行读取文件逻辑
  3. 修正路径与读取逻辑
    不要硬编码/sdcard/,用官方API获取公共存储路径:
    fun getaudio() {
        val publicRoot = Environment.getExternalStorageDirectory().absolutePath
        val dir = File("$publicRoot/Audiobooks/01 Furies of Calderon")
        if (dir.exists() && dir.isDirectory) {
            val files = dir.listFiles()
            Log.d("mytag", "No of files is ${files?.size ?: 0}")
        } else {
            Log.d("mytag", "目录不存在,请检查路径")
        }
    }
    
  4. 安卓11及以上适配
    安卓11以后requestLegacyExternalStorage失效,直接用MediaStore查询公共目录的音频文件即可:
    fun getAudioFromPublicDir() {
        val projection = arrayOf(
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.DISPLAY_NAME,
            MediaStore.Audio.Media.RELATIVE_PATH
        )
        // 筛选对应目录下的音频
        val selection = "${MediaStore.Audio.Media.RELATIVE_PATH} LIKE ?"
        val selectionArgs = arrayOf("%Audiobooks/01 Furies of Calderon/%")
        
        contentResolver.query(
            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
            projection,
            selection,
            selectionArgs,
            null
        )?.use { cursor ->
            val fileCount = cursor.count
            Log.d("mytag", "No of files is $fileCount")
            while (cursor.moveToNext()) {
                val fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME))
                Log.d("mytag", "找到音频:$fileName")
            }
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 02:03:03