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

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()
    }
}

额外注意事项

  1. 播放器实例管理:用remember缓存ExoPlayer实例,避免每次重组都新建播放器:
val player = remember(context) {
    ExoPlayer.Builder(context).build().apply {
        setMediaItem(MediaItem.fromUri("你的视频地址"))
        repeatMode = Player.REPEAT_MODE_OFF
    }
}

// 组件销毁时释放播放器资源
DisposableEffect(Unit) {
    onDispose {
        player.release()
    }
}
  1. 控制栏逻辑校验:在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 14:29:50