Android中MediaPlayer无法播放流式电台链接的问题排查
Android原生MediaPlayer无法播放电台流的问题修复方案
问题诊断
从Logcat错误码可直接定位核心问题:
MediaPlayerNative error (1, -2147483648):要么是数据源的协议/格式不被原生MediaPlayer支持,要么是Android 9+系统默认禁止明文HTTP请求,导致无法加载数据源;MediaPlayer error (-38,0):第二次点击按钮时,旧的MediaPlayer实例未被释放,状态混乱引发的非法状态错误。
你的代码还存在两个致命逻辑问题:
prepareAsync()是异步操作,直接调用start()会在MediaPlayer未准备完成时执行,必然播放失败;- 每次点击都创建新的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 } }
关键说明
- 优先选择ExoPlayer:对于radio-browser.info的电台流,多数采用HLS协议,原生MediaPlayer支持不佳,ExoPlayer能适配绝大多数流媒体协议;
- 严格管理生命周期:页面销毁或切换时必须释放播放器资源,避免内存泄漏和状态错误;
- 必加错误监听:添加错误监听可快速定位播放失败的具体原因,大幅降低调试成本。
内容的提问来源于stack exchange,提问作者Alfred
相关产品推荐
相关产品推荐

