商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 如何在 Android 中正确播放流式音频 URL(如网络电台)

如何在 Android 中正确播放流式音频 URL(如网络电台)

  发布于2026-07-14 阅读(0)

扫一扫,手机访问

聊点干货。在Android上播放网络电台,很多人第一步就踩坑——直接用MediaPlayer.setDataSource(url)去拉一个流,结果往往是静音、无响应,哪怕日志里明晃晃地写着“Audio started playing”。问题的根源在于,流式音频这东西,尤其是Icecast、Shoutcast这类协议,对音频上下文、线程模型和权限的要求,比普通音频文件严格得多。而旧式的写法,比如setAudioStreamType(),早已被标记为弃用,在Android 8.0(API 26)之后的系统里,根本走不通现代媒体策略。

那具体该怎么操作?下面一步步拆解。

✅ 正确实现步骤

1. 权限得先到位

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,既能规避安全限制,兼容性也更好。

2. 用AudioAttributes替代过时的API

自从Android 8.0起,setAudioStreamType()就成了过去式。现在必须通过AudioAttributes来明确告诉系统你要播放什么类型的内容:

val audioAttributes = AudioAttributes.Builder()
    .setUsage(AudioAttributes.USAGE_MEDIA)        // 媒体用途,不是通话或提示音
    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) // 内容类型为音乐
    .build()

3. 后台线程准备,主线程控制播放

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()
        }
    }
}

4. 一些不得不提的最佳实践

  • 释放资源:Activity销毁时,一定要调用mediaPlayer?.release()并置空引用,否则内存泄漏等着你。
  • 状态管理:调用start()前先检查mediaPlayer.isPlaying,避免重复启动——否则会触发-38错误(INVALID_OPERATION),这坑不少人都踩过。
  • 格式兼容性:部分电台流,尤其是Shoutcast协议,可能需要手动设置User-Agent。如果MediaPlayer搞不定,换个思路,直接用ExoPlayer,它对复杂流媒体的支持更完善。
  • 调试技巧:用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强出一个量级。

本文转载于:https://www.php.cn/faq/2814904.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注