
本文详解如何使用 mediaplayer 正确播放 http 流式音频(如 radio-browser.info 提供的电台链接),解决因缺少音频属性配置、线程阻塞或过时 api 导致的静音、崩溃或错误码(如 -38、-2147483648)问题。
本文详解如何使用 mediaplayer 正确播放 http 流式音频(如 radio-browser.info 提供的电台链接),解决因缺少音频属性配置、线程阻塞或过时 api 导致的静音、崩溃或错误码(如 -38、-2147483648)问题。
在 Android 开发中,直接调用 MediaPlayer.setDataSource(url) 播放网络电台流(如 http://play.sas-media.ru/play_256)常会静音无响应,即使日志显示“Audio started playing”。根本原因在于:流式音频(尤其是 Icecast/Shoutcast 类型)对音频上下文、线程模型和权限要求更严格,而旧式写法(如 setAudioStreamType())已弃用且无法满足现代 Android(API 26+)的媒体策略。
✅ 正确实现步骤
1. 添加必要权限
在 AndroidManifest.xml 中声明网络访问权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
⚠️ 注意:Android 9(API 28)及以上默认禁止明文 HTTP 请求。若使用 http:// 链接(如示例中的 http://play.sas-media.ru/play_256),需在 AndroidManifest.xml 的
标签中添加: android:usesCleartextTraffic="true"强烈建议优先使用 HTTPS 流地址(如 https://play.sas-media.ru/play_256),以规避安全限制并提升兼容性。
Android Adb Skill下载Android 开发调试技能,通过系统 ADB 工具操作 Android 设备。以下场景必须触发此技能:(1) 直接 ADB 操作——安装 APK、查看设备列表、抓取 logcat 日志、查看已安装应用、清除应用数据、截图、重启设备、拉取/推送文件、查看 CPU/内存/电池信息、adb shell 操作;(2)...
2. 使用 AudioAttributes 替代已弃用的 setAudioStreamType()
自 Android 8.0(API 26)起,setAudioStreamType() 被标记为 @Deprecated,必须通过 AudioAttributes 显式声明媒体用途:
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) // 表明用于媒体播放(非通话/提示音)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) // 内容类型为音乐
.build()
3. 在后台线程准备 MediaPlayer,主线程控制播放
prepare() 是阻塞操作,绝不可在主线程调用(否则 ANR)。推荐使用 prepareAsync() + OnPreparedListener,或协程配合 withContext(Dispatchers.IO) 安全执行:
private fun playAudio() {
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()
GlobalScope.launch(Dispatchers.Main) {
try {
val mediaPlayer = MediaPlayer().apply {
setAudioAttributes(audioAttributes)
setDataSource("https://play.sas-media.ru/play_256") // ✅ 推荐 HTTPS
}
// 在 IO 线程完成耗时准备
withContext(Dispatchers.IO) {
mediaPlayer.prepare() // 同步 prepare(协程内安全)
}
mediaPlayer.start()
Toast.makeText(this@MainActivity, "Radio is playing", Toast.LENGTH_SHORT).show()
// ? 关键:持有引用并监听异常/完成事件(生产环境必备)
mediaPlayer.setOnErrorListener { mp, what, extra ->
Log.e("MediaPlayer", "Error: $what, Extra: $extra")
Toast.makeText(this@MainActivity, "Playback failed", Toast.LENGTH_LONG).show()
true
}
mediaPlayer.setOnCompletionListener {
Toast.makeText(this@MainActivity, "Stream ended", Toast.LENGTH_SHORT).show()
}
} catch (e: IOException) {
Log.e("MediaPlayer", "Failed to prepare", e)
Toast.makeText(this@MainActivity, "Network or format error", Toast.LENGTH_LONG).show()
}
}
}
4. 补充最佳实践
- 释放资源:Activity 销毁时务必调用 mediaPlayer?.release() 并置空引用,防止内存泄漏;
- 状态管理:检查 mediaPlayer.isPlaying 避免重复 start()(否则触发 -38 错误:INVALID_OPERATION);
- 格式兼容性:部分电台流(如 Shoutcast)需手动设置 User-Agent,此时应改用 ExoPlayer(MediaPlayer 对复杂流支持有限);
-
调试技巧:通过 adb logcat | grep -i media 观察 MediaPlayerNative 日志,错误码对照:
- (1, -2147483648) → 通常为 PREPARE_TIMEOUT 或 MEDIA_ERROR_UNKNOWN,多因网络/协议不支持;
- (-38, 0) → INVALID_OPERATION,常见于未准备就调用 start() 或重复操作。
总结
播放流式电台 URL 的核心是:权限到位 + HTTPS 优先 + AudioAttributes 正确配置 + 异步准备 + 错误兜底。避免使用已弃用 API,善用协程隔离 IO 操作,并始终监听播放状态与异常。对于高可靠性需求(如长时间后台播放、多格式支持),建议升级至 ExoPlayer,它对流媒体协议(HLS、DASH、Icecast)的支持更完善、调试能力更强。











