Kotlin应用读取外部存储音频触发FileNotFoundException异常原因求解
问题原因
- 核心错误是混淆了Content URI和本地文件路径的概念:从系统选择Intent返回的
data.data是content://开头的内容URI,并不是文件的真实存储路径,调用uri.path拿到的只是URI自身的路径片段,不是本地文件系统的有效路径,直接用该路径新建File对象必然找不到对应文件,触发FileNotFoundException。 - 适配问题:如果应用targetSdk >= 30(Android 11及以上),即便拿到了文件真实路径,没有
MANAGE_EXTERNAL_STORAGE权限的前提下,也无法直接通过File API访问公共存储的媒体文件。 - 权限缺失:Android 6.0及以上设备如果没有动态申请对应存储读取权限,也会导致读取文件失败抛出该异常。
修复方案
你完全不需要将URI转为File对象,ContentResolver本身支持直接通过原始内容URI打开输入流,修改逻辑如下:
- 优先存储原始URI,不要提取path字段
// 观察ViewModel时直接保存URI,不要转路径字符串 var newAudioUri: Uri? = null newAudioViewModel!!.getNewAudio().observe(viewLifecycleOwner, Observer<Uri?> { audio -> newAudioUri = audio })
- 直接通过URI打开输入流构造RequestBody
newAudioUri?.let { uri -> context?.contentResolver?.openInputStream(uri)?.use { inputStream -> val requestFile = RequestBody.create(MediaType.parse("audio/mpeg"), inputStream.readBytes()) // 后续处理requestFile的业务逻辑 } ?: throw IOException("无法打开对应URI的输入流") }
补充注意事项
- 权限适配:Android 6.0-10需要动态申请
READ_EXTERNAL_STORAGE权限,Android 13及以上需要动态申请READ_MEDIA_AUDIO权限。 - 资源管理:用
use方法包裹输入流操作,会在使用结束后自动关闭流,避免资源泄漏。 - 空安全优化:尽量避免用
!!强制空断言,优先使用?.、let等安全调用方式,避免空指针崩溃。
内容的提问来源于stack exchange,提问作者Daniil Andreev
相关产品推荐
相关产品推荐

