移动端开发新手:getExternalStorageDirectory返回异常路径的原因及解决方法
关于Android存储路径异常及音频文件扫描的解决方案
嘿,作为移动端开发新手碰到这个问题真的太常见了,我来帮你把这个问题掰碎了讲清楚~
为什么会返回"/storage/3139-6333"?
首先,这个路径其实是**可移除外部存储(也就是物理SD卡)**的标识路径。Android从4.4版本开始,外部存储被分成了两类:
- 内置外部存储:手机自带的、不可移除的扩展存储(通常我们误以为的"SD卡"其实是这个)
- 可移除外部存储:真正的物理SD卡
getExternalStorageDirectory()这个方法的核心问题在于,它返回的是系统认定的"默认外部存储"——如果你的设备插了SD卡,部分厂商的系统会把SD卡设为默认,就会返回这种带UUID的路径。更关键的是,从Android Q(API 29)开始,这个方法已经被官方废弃了,因为Scoped Storage(分区存储)的引入,直接通过路径访问存储的方式会越来越受限。
解决办法:放弃直接路径扫描,用官方推荐的MediaStore查询音频
既然要扫描设备上的所有音频文件,MediaStore才是正确的打开方式——它是Android系统提供的媒体内容索引库,能帮你快速找到所有音频,还不用处理复杂的路径适配问题。
第一步:申请必要权限
- Android 6.0(API 23)到Android 12(API 32):需要动态申请
READ_EXTERNAL_STORAGE权限 - Android 13(API 33)及以上:针对音频文件,申请
READ_MEDIA_AUDIO权限即可(权限更细分)
第二步:用MediaStore查询音频文件
这里给你一段Kotlin的示例代码,直接可以用来筛选.mp3、.m4a、.wav格式的音频:
// 确保已经获取权限后执行这段代码 val projection = arrayOf( MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DISPLAY_NAME, // Android Q及以上,DATA字段被废弃,改用RELATIVE_PATH if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Audio.Media.RELATIVE_PATH } else { MediaStore.Audio.Media.DATA } ) // 通过MIME类型筛选音频格式,比判断后缀更可靠 val selection = "${MediaStore.Audio.Media.MIME_TYPE} IN (?, ?, ?)" val selectionArgs = arrayOf( "audio/mpeg", // .mp3对应的MIME类型 "audio/mp4", // .m4a对应的MIME类型 "audio/wav" // .wav对应的MIME类型 ) contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null // 可以添加排序规则,比如按修改时间排序 )?.use { cursor -> while (cursor.moveToNext()) { val fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)) val filePath = cursor.getString(cursor.getColumnIndexOrThrow(projection[2])) // 这里就可以拿到你需要的音频文件信息了,比如存到列表里 Log.d("MusicPlayer", "找到音频文件:$fileName,路径:$filePath") } }
如果确实需要访问SD卡上的文件怎么办?
如果你的需求必须要访问物理SD卡的内容,那得用StorageManager配合**Storage Access Framework(SAF)**来获取访问权限,因为Scoped Storage限制了直接访问SD卡路径:
val storageManager = getSystemService(Context.STORAGE_SERVICE) as StorageManager val storageVolumes = storageManager.storageVolumes // 遍历所有存储卷,找到可移除的SD卡 for (volume in storageVolumes) { if (volume.isRemovable) { // 打开系统文件选择器,让用户授权访问SD卡 val intent = volume.createOpenDocumentTreeIntent() startActivityForResult(intent, REQUEST_CODE_SD_CARD_ACCESS) } } // 在onActivityResult(或registerForActivityResult)中处理授权结果 // 授权后,就可以通过SAF的Uri来访问SD卡内容了
总结一下
- 你遇到的路径问题本质是
getExternalStorageDirectory()的适配缺陷+已废弃导致的,它可能指向物理SD卡而非内置存储 - 扫描音频文件的最优解是用MediaStore,既符合官方规范,又不用操心路径差异
- 若需访问SD卡,必须通过SAF获取用户授权,不能再直接用路径访问
内容的提问来源于stack exchange,提问作者Daniel Kargl
相关产品推荐
相关产品推荐

