自定义Android应用集成Alexa Voice Service及MP3播放问题排查
解决AVS响应MP3文件MediaPlayer.prepare()报错问题
看起来你已经在AVS集成的路上走了不少关键步骤——成功登录获取token、实现本地录音功能、发送请求并收到AVS响应,现在卡在了播放返回的MP3文件这一步。针对MediaPlayer.prepare()报错的问题,咱们一步步排查解决:
1. 先确认MP3文件本身是否有效
这是最基础的排查点:
- 把你保存的
speech2.mp3导出到电脑,用系统播放器或者专业音频工具(比如Audacity)打开试试。如果文件打不开、播放失真,说明AVS响应的解析或文件保存过程出了问题。 - 重点检查multipart响应的解析逻辑:AVS返回的是multipart格式的响应,你需要正确识别boundary,只提取音频类型的part数据,别把multipart的头信息或其他JSON部分的内容写入MP3文件里。可以用OkHttp的
MultipartReader来规范解析,示例代码如下:
fun parseAVSResponse(response: Response) { val body = response.body ?: return val contentType = body.contentType() ?: return val boundary = getBoundary(contentType.toString()) ?: return val reader = MultipartReader(body.source(), boundary) while (reader.hasNext()) { val part = reader.nextPart() val contentTypeHeader = part.headers["Content-Type"] // 只处理音频类型的part if (contentTypeHeader?.contains("audio/mpeg") == true) { val audioFile = File(appcontext?.getExternalFilesDir(Environment.DIRECTORY_MUSIC), "speech2.mp3") val sink = audioFile.sink().buffer() sink.writeAll(part.source()) sink.close() break } // 跳过非音频的part(比如JSON响应) part.source().skip(Long.MAX_VALUE) } }
2. 修复存储权限问题
Android 10及以上版本对外部存储的访问做了严格限制,直接写入根目录可能会遇到权限问题,导致文件写入不完整或无法读取:
- 改用应用私有存储目录,比如
getExternalFilesDir(),这个目录不需要额外申请权限,代码示例:
// 替换原来的文件路径 val audioFile = File(appcontext?.getExternalFilesDir(Environment.DIRECTORY_MUSIC), "speech2.mp3")
3. 优化MediaPlayer的使用逻辑
你的playRecord()代码缺少错误捕获和资源释放,而且没有设置合适的音频属性,这也可能导致prepare失败:
fun playRecord() { doAsync { val appContext = appcontext ?: return@doAsync val audioFile = File(appContext.getExternalFilesDir(Environment.DIRECTORY_MUSIC), "speech2.mp3") if (!audioFile.exists()) { appContext.runOnUiThread { toast("音频文件不存在") } return@doAsync } val mediaPlayer = MediaPlayer() try { // 设置合适的音频属性 val audioAttributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build() mediaPlayer.setAudioAttributes(audioAttributes) mediaPlayer.setDataSource(audioFile.path) mediaPlayer.prepare() mediaPlayer.start() // 播放完成后释放资源 mediaPlayer.setOnCompletionListener { mp -> mp.release() } } catch (e: IOException) { Log.e("SoundRecorder", "播放失败: ${e.message}", e) appContext.runOnUiThread { toast("播放失败: ${e.message}") } mediaPlayer.release() } } }
4. 额外检查:AVS请求的音频格式是否合规
AVS要求上传的音频必须是PCM 16bit、16kHz采样率、单声道,你的录音代码里已经设置了这些参数,但要确保发送请求时没有修改格式(比如不要把PCM转换成其他格式再发送)。
按照上面的步骤排查,应该能解决prepare报错的问题。如果还是不行,建议打印IOException的具体堆栈信息,这能帮你定位更细节的问题。
内容的提问来源于stack exchange,提问作者anoop4real
相关产品推荐
相关产品推荐

