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

Jetpack Compose中添加onPlayerStateChanged监听器后ExoPlayer重播按钮失效

问题:ExoPlayer重播按钮在视频结束后失效,显示下一页按钮触发重组导致异常

问题概述

创建了ExoPlayer实例和重播按钮,按钮在视频播放过程中正常工作,但视频结束后完全失效。推测是视频结束时调用viewModel.showNextButton()触发UI重组,破坏了原有的ExoPlayer引用。

核心问题分析

  1. ExoPlayer实例未跨重组保留:原代码中ExoPlayer没有用remember包裹,每次UI重组都会重新创建实例,重播按钮引用的是旧的已失效实例。
  2. ViewModel状态更新逻辑错误:直接修改StateFlow内部的MutableState值,不符合StateFlow的不可变数据更新规范,同时状态收集方式错误,无法正确响应变化。
  3. UI状态引用错误:显示下一页按钮时引用了未定义的viewModel.showButton.value,属于代码笔误。

修复方案

1. 用remember保留ExoPlayer实例

在Compose中,需要跨重组保留的对象必须用remember包裹,确保重播按钮始终引用同一个有效实例:

val context = LocalContext.current

// 用remember保存ExoPlayer,避免重组时重新创建
val exoPlayer = remember {
    ExoPlayer.Builder(context)
        .build()
        .also { player ->
            val mediaItem = MediaItem.Builder()
                .setUri(Uri.parse("android.resource://$packageName/${R.raw.xxxxxx}"))
                .build()
            player.setMediaItem(mediaItem)
            player.playWhenReady = true
            player.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
            player.addListener(playerEventListener)
            player.prepare()
        }
}

DisposableEffect(Unit) {
    onDispose { exoPlayer.release() }
}

AndroidView(factory = {
    StyledPlayerView(context).apply {
        hideController()
        useController = false
        player = exoPlayer
    }
})

2. 修正ViewModel状态更新逻辑

将UiState改为不可变数据类,通过复制整个对象更新状态,符合StateFlow的使用规范:

// 不可变UiState,用普通Boolean替代MutableState
data class TutorialUiState(
    val showNextButton: Boolean = false
)

class TutorialViewModel(application: HDiagsApplication) : AndroidViewModel(application) {
    private val _uiState = MutableStateFlow(TutorialUiState())
    val uiState = _uiState.asStateFlow()

    fun showNextButton() {
        // 通过复制更新整个UiState
        _uiState.value = _uiState.value.copy(showNextButton = true)
    }

    // 可选:添加隐藏按钮的方法,用于重播时重置状态
    fun hideNextButton() {
        _uiState.value = _uiState.value.copy(showNextButton = false)
    }
}

3. 正确收集ViewModel状态

用collectAsState收集StateFlow的状态更新,确保UI能正确响应变化:

// 正确收集StateFlow状态
val uiState by viewModel.uiState.collectAsState()

// 显示下一页按钮
if (uiState.showNextButton) {
    Button(onClick = {
        startActivity(Intent(this@TutorialJetpack, ParadigmControllerJetpack::class.java))
    }) {
        Text(text = "Begin test")
    }
}

4. 优化重播按钮逻辑

确保重播时重置播放状态,可选隐藏下一页按钮:

Button(onClick = {                             
    exoPlayer.seekTo(0)
    exoPlayer.playWhenReady = true
    // 重播时隐藏下一页按钮
    viewModel.hideNextButton()
}){
    Text(
        text = "replay",
        fontSize = 18.sp,
        modifier = Modifier.padding(5.dp),
        textAlign = TextAlign.Center
    )
}

内容的提问来源于stack exchange,提问作者Luke Dawe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 04:17:04