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

Jetpack Compose中ExoPlayer切换视频后视图不更新问题

问题分析与解决

核心问题

你的VideoPlayer组件存在两个关键问题导致视频切换失效:

  1. exoPlayer通过remember(context)初始化,仅当context变化时才会重新创建。当videoId更新时,既不会触发播放器实例重建,也没有逻辑更新现有播放器的媒体源。
  2. AndroidView仅在首次初始化时绑定播放器,后续videoId变化时无任何更新逻辑,导致播放器内容不刷新。

解决方案(推荐复用播放器的高性能方案)

这种方式避免频繁创建销毁ExoPlayer,而是在videoId变化时动态替换媒体源:

@OptIn(UnstableApi::class)
@Composable
fun VideoPlayer(videoId: Int) {
    val context = LocalContext.current

    // 保留ExoPlayer实例,仅在context变化时重建(如配置变更)
    val exoPlayer = remember(context) {
        ExoPlayer.Builder(context).build()
    }

    // 监听videoId变化,更新媒体源
    LaunchedEffect(videoId) {
        val dataSourceFactory: DataSource.Factory = DefaultDataSource.Factory(context, DefaultHttpDataSource.Factory())
        val uriSource = Uri.parse(
            "android.resource://" + context.packageName + "/" + videoId
        )
        val mediaItem = MediaItem.fromUri(uriSource)
        exoPlayer.setMediaItem(mediaItem)
        exoPlayer.prepare()
        exoPlayer.play() // 可选:切换后自动播放
    }

    // 组件销毁时释放播放器资源,防止内存泄漏
    DisposableEffect(Unit) {
        onDispose {
            exoPlayer.release()
        }
    }

    AndroidView(
        factory = { ctxt ->
            PlayerView(ctxt).apply {
                player = exoPlayer
            }
        },
        // 确保videoId变化时View能关联到最新的播放器状态
        update = { view ->
            view.player = exoPlayer
        }
    )
}

备选方案(简单直接,适合场景单一的情况)

如果你的视频切换频率不高,也可以通过将videoId加入remember依赖,让videoId变化时直接重建播放器:

@OptIn(UnstableApi::class)
@Composable
fun VideoPlayer(videoId: Int) {
    val context = LocalContext.current

    // videoId变化时自动重建ExoPlayer
    val exoPlayer = remember(videoId, context) {
        ExoPlayer.Builder(context).build().apply {
            val dataSourceFactory: DataSource.Factory = DefaultDataSource.Factory(context, DefaultHttpDataSource.Factory())
            val uriSource = Uri.parse(
                "android.resource://" + context.packageName + "/" + videoId
            )
            val source = ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(MediaItem.fromUri(uriSource))
            this.setMediaSource(source)
            this.prepare()
        }
    }

    // 销毁时释放资源
    DisposableEffect(Unit) {
        onDispose {
            exoPlayer.release()
        }
    }

    AndroidView(
        factory = { ctxt ->
            PlayerView(ctxt).apply {
                player = exoPlayer
            }
        }
    )
}

额外检查点

  • 确认VideoModel.getVideoId()返回的是正确的raw资源ID,避免取值错误导致媒体源无效。
  • 若不需要自动播放,可移除exoPlayer.play()调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:42:36