发布于2026-07-14 阅读(0)
扫一扫,手机访问
聊点干货。在Android上播放网络电台,很多人第一步就踩坑——直接用MediaPlayer.setDataSource(url)去拉一个流,结果往往是静音、无响应,哪怕日志里明晃晃地写着“Audio started playing”。问题的根源在于,流式音频这东西,尤其是Icecast、Shoutcast这类协议,对音频上下文、线程模型和权限的要求,比普通音频文件严格得多。而旧式的写法,比如setAudioStreamType(),早已被标记为弃用,在Android 8.0(API 26)之后的系统里,根本走不通现代媒体策略。
那具体该怎么操作?下面一步步拆解。
在AndroidManifest.xml里,把网络访问权限声明清楚:
⚠️ 这里要特别提醒一下,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 8.0起,setAudioStreamType()就成了过去式。现在必须通过AudioAttributes来明确告诉系统你要播放什么类型的内容:
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) // 媒体用途,不是通话或提示音
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) // 内容类型为音乐
.build()
prepare()是阻塞操作,绝对不能放在主线程上跑,否则就是ANR的命。推荐的做法是用prepareAsync()配合OnPreparedListener,或者干脆用协程在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()
}
}
}
mediaPlayer?.release()并置空引用,否则内存泄漏等着你。start()前先检查mediaPlayer.isPlaying,避免重复启动——否则会触发-38错误(INVALID_OPERATION),这坑不少人都踩过。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等协议的支持,以及调试能力,都比MediaPlayer强出一个量级。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8