You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中MediaPlayer无法播放流式电台链接的问题排查

Android原生MediaPlayer无法播放电台流的问题修复方案

问题诊断

从Logcat错误码可直接定位核心问题:

  • MediaPlayerNative error (1, -2147483648):要么是数据源的协议/格式不被原生MediaPlayer支持,要么是Android 9+系统默认禁止明文HTTP请求,导致无法加载数据源;
  • MediaPlayer error (-38,0):第二次点击按钮时,旧的MediaPlayer实例未被释放,状态混乱引发的非法状态错误。

你的代码还存在两个致命逻辑问题:

  1. prepareAsync()是异步操作,直接调用start()会在MediaPlayer未准备完成时执行,必然播放失败;
  2. 每次点击都创建新的MediaPlayer实例,旧实例未释放,导致资源泄漏和状态冲突。

解决方案

方案1:修复原生MediaPlayer代码

步骤1:允许明文HTTP请求(Android 9+)

在AndroidManifest.xml的<application>标签中添加配置,允许明文网络请求:

android:usesCleartextTraffic="true"

步骤2:正确管理MediaPlayer状态与回调

修改Activity代码,添加状态监听并严格管理播放器生命周期:

class MainActivity : AppCompatActivity() {
    private lateinit var testingButton: Button
    private var mediaPlayer: MediaPlayer? = null
    private var isPlaying = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        testingButton = findViewById(R.id.testingButton)

        testingButton.setOnClickListener {
            if (isPlaying) {
                stopAudio()
            } else {
                playAudio()
            }
        }
    }

    private fun playAudio() {
        // 先释放旧实例,避免状态冲突
        mediaPlayer?.release()
        mediaPlayer = MediaPlayer().apply {
            // 设置音频属性,确保系统正确识别播放场景
            setAudioAttributes(
                AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .build()
            )
            try {
                setDataSource("http://play.sas-media.ru/play_256")
                prepareAsync()
                // 准备完成后再启动播放
                setOnPreparedListener {
                    it.start()
                    isPlaying = true
                    testingButton.text = "停止播放"
                    Toast.makeText(this@MainActivity, "音频开始播放", Toast.LENGTH_LONG).show()
                }
                // 错误监听,快速定位问题
                setOnErrorListener { _, what, extra ->
                    Log.e("MediaPlayerError", "错误码: $what, 额外信息: $extra")
                    Toast.makeText(this@MainActivity, "播放失败", Toast.LENGTH_LONG).show()
                    isPlaying = false
                    testingButton.text = "开始播放"
                    true
                }
            } catch (e: IOException) {
                e.printStackTrace()
                Toast.makeText(this@MainActivity, "加载数据源失败", Toast.LENGTH_LONG).show()
            }
        }
    }

    private fun stopAudio() {
        mediaPlayer?.stop()
        mediaPlayer?.release()
        mediaPlayer = null
        isPlaying = false
        testingButton.text = "开始播放"
        Toast.makeText(this, "音频已停止", Toast.LENGTH_LONG).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        // 页面销毁时强制释放资源,避免内存泄漏
        mediaPlayer?.release()
        mediaPlayer = null
    }
}

方案2:使用ExoPlayer(推荐,适配更多流媒体协议)

原生MediaPlayer对HLS、MPEG-DASH等电台常用流媒体协议支持有限,ExoPlayer是Google官方推荐的播放器,更适合流媒体场景,能完美适配radio-browser.info的绝大多数电台流。

步骤1:添加依赖

在build.gradle(Module level)的dependencies中引入ExoPlayer依赖:

implementation "com.google.android.exoplayer:exoplayer-core:2.19.1"
implementation "com.google.android.exoplayer:exoplayer-ui:2.19.1"

步骤2:布局中添加PlayerView

在activity_main.xml中添加播放器视图(可选,也可纯代码实现):

<com.google.android.exoplayer2.ui.PlayerView
    android:id="@+id/playerView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/testingButton"/>

步骤3:Activity代码实现

class MainActivity : AppCompatActivity() {
    private lateinit var testingButton: Button
    private lateinit var playerView: PlayerView
    private var player: SimpleExoPlayer? = null
    private var isPlaying = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        testingButton = findViewById(R.id.testingButton)
        playerView = findViewById(R.id.playerView)

        testingButton.setOnClickListener {
            if (isPlaying) {
                stopAudio()
            } else {
                playAudio()
            }
        }
    }

    private fun playAudio() {
        // 创建ExoPlayer实例
        player = SimpleExoPlayer.Builder(this).build().apply {
            playerView.player = this
            // 创建媒体源
            val mediaItem = MediaItem.fromUri("http://play.sas-media.ru/play_256")
            setMediaItem(mediaItem)
            prepare()
            playWhenReady = true
            // 监听播放状态
            addListener(object : Player.Listener {
                override fun onPlaybackStateChanged(playbackState: Int) {
                    super.onPlaybackStateChanged(playbackState)
                    if (playbackState == Player.STATE_READY) {
                        isPlaying = true
                        testingButton.text = "停止播放"
                        Toast.makeText(this@MainActivity, "音频开始播放", Toast.LENGTH_LONG).show()
                    } else if (playbackState == Player.STATE_ENDED || playbackState == Player.STATE_IDLE) {
                        isPlaying = false
                        testingButton.text = "开始播放"
                    }
                }

                override fun onPlayerError(error: PlaybackException) {
                    super.onPlayerError(error)
                    Log.e("ExoPlayerError", error.message.toString())
                    Toast.makeText(this@MainActivity, "播放失败", Toast.LENGTH_LONG).show()
                    isPlaying = false
                    testingButton.text = "开始播放"
                }
            })
        }
    }

    private fun stopAudio() {
        player?.stop()
        player?.release()
        player = null
        isPlaying = false
        testingButton.text = "开始播放"
        Toast.makeText(this, "音频已停止", Toast.LENGTH_LONG).show()
    }

    override fun onDestroy() {
        super.onDestroy()
        player?.release()
        player = null
    }
}

关键说明

  1. 优先选择ExoPlayer:对于radio-browser.info的电台流,多数采用HLS协议,原生MediaPlayer支持不佳,ExoPlayer能适配绝大多数流媒体协议;
  2. 严格管理生命周期:页面销毁或切换时必须释放播放器资源,避免内存泄漏和状态错误;
  3. 必加错误监听:添加错误监听可快速定位播放失败的具体原因,大幅降低调试成本。

内容的提问来源于stack exchange,提问作者Alfred

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:12:05