ExoPlayer 3 + Jetpack Compose:暂停后播放视频重复问题求助
解决Jetpack Compose + ExoPlayer 3 暂停后播放视频重复问题
问题核心原因
你用LaunchedEffect配合while(true)循环轮询视频currentTime的方式,会引发两个关键问题:
- 播放器暂停后,循环仍在后台持续运行,未及时终止;
- 再次播放时,若
LaunchedEffect的触发key设置不当,会启动新的协程循环,新旧协程同时操作播放器,引发重复播放逻辑。
解决方案
方案1:修复LaunchedEffect循环逻辑,确保协程正确终止
修改PlayerScreen.kt中的LaunchedEffect代码,利用协程的isActive属性控制循环生命周期,同时仅在播放状态下更新时间:
// 在PlayerScreen的Composable函数内 val player = remember { /* 你的ExoPlayer实例初始化逻辑 */ } var currentVideoTime by remember { mutableLongStateOf(0L) } LaunchedEffect(player) { // 仅当协程处于活跃状态时执行循环 while (isActive) { // 仅在播放器播放时更新时间,减少无效操作 if (player.isPlaying) { currentVideoTime = player.currentPosition } // 控制刷新频率,避免过于频繁的UI更新 delay(100) } }
isActive是LaunchedEffect协程的内置属性,当Compose组件销毁、LaunchedEffect因key变化重启时,旧协程会被取消,isActive变为false,循环自动终止,避免残留协程干扰。
方案2:使用ExoPlayer原生回调替代手动轮询(更推荐)
ExoPlayer提供了原生的状态监听接口,无需手动轮询,效率更高且避免协程泄漏问题。在PlayerScreen.kt中用DisposableEffect管理监听器生命周期:
// 在PlayerScreen的Composable函数内 val player = remember { /* 你的ExoPlayer实例初始化逻辑 */ } var currentVideoTime by remember { mutableLongStateOf(0L) } DisposableEffect(player) { val playerListener = object : Player.Listener { // 监听播放状态变化 override fun onPlaybackStateChanged(playbackState: Int) { if (playbackState == Player.STATE_READY && player.isPlaying) { // 可选:播放开始时的初始化操作 } } // 监听播放位置变化 override fun onPositionDiscontinuity(reason: Int) { currentVideoTime = player.currentPosition } // 定时刷新时间的实现 override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) { if (playWhenReady) { player.addPeriodicTimer( periodMs = 100, callback = { currentVideoTime = player.currentPosition } ) } else { player.removePeriodicTimers() } } } player.addListener(playerListener) // 组件销毁时移除监听器,避免内存泄漏 onDispose { player.removeListener(playerListener) player.removePeriodicTimers() } }
额外注意事项
- 播放器实例管理:用
remember缓存ExoPlayer实例,避免每次重组都新建播放器:
val player = remember(context) { ExoPlayer.Builder(context).build().apply { setMediaItem(MediaItem.fromUri("你的视频地址")) repeatMode = Player.REPEAT_MODE_OFF } } // 组件销毁时释放播放器资源 DisposableEffect(Unit) { onDispose { player.release() } }
- 控制栏逻辑校验:在
ControllerScreen.kt中确保播放/暂停操作唯一,避免重复调用player.play():
// ControllerScreen中的播放按钮点击逻辑 IconButton( onClick = { if (player.isPlaying) { player.pause() } else { // 仅当播放器未播放时调用play() player.play() } } ) { Icon( imageVector = if (player.isPlaying) Icons.Default.Pause else Icons.Default.PlayArrow, contentDescription = if (player.isPlaying) "暂停" else "播放" ) }
内容的提问来源于stack exchange,提问作者Angga7Togk
相关产品推荐
相关产品推荐

