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

ExoPlayer的PlayerView重启Activity后黑屏问题求助

问题:ExoPlayer重启Activity后黑屏无法自动播放

我用ExoPlayer开发视频播放器,在finish Activity并重新打开时,PlayerView偶尔会出现黑屏。点击播放/暂停按钮后仍保持黑屏,但点击快进/快退5秒按钮后视频可正常播放。需求是视频就绪后自动播放且隐藏控制器。

日志显示:正常播放时,playbackState变为STATE_READY(值为3)且isPlaying为true;黑屏时,playbackState在STATE_READY后变为STATE_BUFFERING(值为2)且isPlaying为false,无异常抛出。尝试在isPlaying为false时调用vv.player?.play()方法,但无效。

原代码

class PreviewActivity : BaseActivity<ActivityPreviewBinding, ActivityPreviewViewModel>() {
    override fun getViewBinding(): ActivityPreviewBinding =
        ActivityPreviewBinding.inflate(layoutInflater)

    override fun getViewModelClass(): Class<ActivityPreviewViewModel> =
        ActivityPreviewViewModel::class.java

    @OptIn(UnstableApi::class)
    override fun setupListener() {
        binding.apply {
            vv.player?.addListener(object : Player.Listener {
                override fun onIsPlayingChanged(isPlaying: Boolean) {
                    Log.d(TAG, "[onIsPlayingChanged] IsPlaying: $isPlaying")
                    if (!isPlaying) {
                        vv.player?.play()
                    }
                }

                override fun onPlayerError(error: PlaybackException) {
                    super.onPlayerError(error)
                    Log.e(TAG, "[onPlayerError] Error: ${error.message}", error)
                }

                override fun onPlaybackStateChanged(playbackState: Int) {
                    super.onPlaybackStateChanged(playbackState)
                    Log.d(TAG, "[onPlaybackStateChanged] PlaybackState: $playbackState")
                }
            })
        }
    }

    override fun setupViews() {
        val bundle = intent.extras
        val videoPath = bundle?.getString(BUNDLE_VIDEO_PATH)
        setupVideo(videoPath)
    }

    @OptIn(UnstableApi::class)
    private fun setupVideo(videoPath: String?) {
        try {
            binding.vv.apply {
                val uri = Uri.parse(videoPath)
                val mediaItem = MediaItem.fromUri(uri)
                val exoPlayer = ExoPlayer.Builder(this@PreviewActivity).build()
                exoPlayer.repeatMode = ExoPlayer.REPEAT_MODE_ONE
                exoPlayer.playWhenReady = true
                exoPlayer.addMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.addAnalyticsListener(EventLogger())
                useController = false
                resizeMode = RESIZE_MODE_ZOOM
                player = exoPlayer
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }


    override fun onDestroy() {
        super.onDestroy()
        binding.vv.player?.clearMediaItems()
        binding.vv.player?.clearVideoSurface()
        binding.vv.player?.release()
    }

    companion object {
        const val BUNDLE_VIDEO_PATH = "videoPath"
        private const val ONE_SECOND = 1000L
        private const val TAG = "PreviewActivity"
    }
}

问题分析

  1. 监听绑定时机错误:setupListener()在setupViews()之前调用(均在onCreate中),此时vv.player尚未初始化,导致监听根本未绑定到播放器实例,后续的onIsPlayingChanged逻辑完全无效。
  2. 播放器状态处理缺失:当播放器进入STATE_READY后又切换到STATE_BUFFERING并卡住时,没有主动触发状态刷新,导致视频表面无法渲染。
  3. playWhenReady设置顺序不合理:在prepare()之前设置playWhenReady,可能与播放器准备流程中的状态冲突,导致自动播放失效。

解决方案

修改后的代码

class PreviewActivity : BaseActivity<ActivityPreviewBinding, ActivityPreviewViewModel>() {
    override fun getViewBinding(): ActivityPreviewBinding =
        ActivityPreviewBinding.inflate(layoutInflater)

    override fun getViewModelClass(): Class<ActivityPreviewViewModel> =
        ActivityPreviewViewModel::class.java

    override fun setupViews() {
        val bundle = intent.extras
        val videoPath = bundle?.getString(BUNDLE_VIDEO_PATH)
        setupVideo(videoPath)
    }

    @OptIn(UnstableApi::class)
    private fun setupVideo(videoPath: String?) {
        try {
            binding.vv.apply {
                val uri = Uri.parse(videoPath)
                val mediaItem = MediaItem.fromUri(uri)
                val exoPlayer = ExoPlayer.Builder(this@PreviewActivity).build()
                exoPlayer.repeatMode = ExoPlayer.REPEAT_MODE_ONE
                exoPlayer.addMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.addAnalyticsListener(EventLogger())
                // 调整playWhenReady到prepare之后,符合ExoPlayer推荐流程
                exoPlayer.playWhenReady = true
                useController = false
                resizeMode = RESIZE_MODE_ZOOM
                player = exoPlayer

                // 直接在播放器初始化后绑定监听,确保实例正确
                exoPlayer.addListener(object : Player.Listener {
                    override fun onIsPlayingChanged(isPlaying: Boolean) {
                        Log.d(TAG, "[onIsPlayingChanged] IsPlaying: $isPlaying")
                    }

                    override fun onPlayerError(error: PlaybackException) {
                        super.onPlayerError(error)
                        Log.e(TAG, "[onPlayerError] Error: ${error.message}", error)
                    }

                    override fun onPlaybackStateChanged(playbackState: Int) {
                        super.onPlaybackStateChanged(playbackState)
                        Log.d(TAG, "[onPlaybackStateChanged] PlaybackState: $playbackState")
                        // 当播放器就绪但未播放时,主动触发播放并刷新表面
                        if (playbackState == Player.STATE_READY && !exoPlayer.isPlaying) {
                            exoPlayer.play()
                            binding.vv.invalidate()
                        }
                    }
                })
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        binding.vv.player?.run {
            clearMediaItems()
            clearVideoSurface()
            release()
        }
        // 置空PlayerView的player引用,避免内存泄漏
        binding.vv.player = null
    }

    companion object {
        const val BUNDLE_VIDEO_PATH = "videoPath"
        private const val TAG = "PreviewActivity"
    }
}

修改说明

  • 移除原setupListener()方法,将监听直接绑定到刚初始化的播放器实例,确保监听生效。
  • 在onPlaybackStateChanged中处理STATE_READY状态,主动调用play()并刷新PlayerView,解决黑屏和自动播放失效问题。
  • 调整playWhenReady到prepare()之后,符合ExoPlayer的推荐初始化顺序。
  • 在onDestroy中增加binding.vv.player = null,避免PlayerView持有已释放的播放器引用,防止内存泄漏。

内容的提问来源于stack exchange,提问作者Vũ Nguyễn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 11:44:53