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" } }
问题分析
- 监听绑定时机错误:
setupListener()在setupViews()之前调用(均在onCreate中),此时vv.player尚未初始化,导致监听根本未绑定到播放器实例,后续的onIsPlayingChanged逻辑完全无效。 - 播放器状态处理缺失:当播放器进入
STATE_READY后又切换到STATE_BUFFERING并卡住时,没有主动触发状态刷新,导致视频表面无法渲染。 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
相关产品推荐
相关产品推荐

