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

如何在Compose的VerticalPager中用AndroidView的onReset复用ExoPlayer并解决OOM

在Jetpack Compose VerticalPager中复用ExoPlayer解决内存溢出问题

你的代码出现内存溢出主要是因为播放器资源未正确释放、重复创建不必要的对象、PlayerView与ExoPlayer的绑定逻辑混乱这几个问题,结合VerticalPager的回收机制,调整方案如下:


核心问题分析

  1. ExoPlayer实例管理混乱:多个PlayerView绑定同一个exoPlayer实例,且未在视图回收时解绑释放,导致播放器资源一直占用内存。
  2. 重复创建对象:onReset中每次都新建DataSourceFactory,大量重复对象堆积引发内存问题。
  3. onRelease未做实质释放:仅打印日志,未处理PlayerView与播放器的解绑及资源回收。

调整后的实现代码

// 在Compose组件中,用remember持有复用的DataSourceFactory,避免重复创建
val dataSourceFactory = remember {
    val defaultDataSourceFactory = DefaultDataSource.Factory(LocalContext.current)
    DefaultDataSource.Factory(LocalContext.current, defaultDataSourceFactory)
}

// 为VerticalPager的每个item设置唯一key,确保AndroidView的onReset正确触发
VerticalPager(
    count = videoList.size,
    key = { videoList[it].id } // 替换为你的视频唯一标识
) { index ->
    val video = videoList[index]
    AndroidView(
        factory = { context ->
            Logger.d { "PlayerView factory created" }
            // 为每个PlayerView创建独立的ExoPlayer实例
            val player = ExoPlayer.Builder(context).build().apply {
                repeatMode = REPEAT_MODE_ONE
            }
            PlayerView(context).apply {
                hideController()
                useController = false
                this.player = player
                resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
                layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
                clipToOutline = true
                // 把Player和View绑定存储,方便后续reset和release
                tag = player
            }
        },
        onReset = { playerView ->
            Logger.d { "PlayerView reset for video: ${video.url}" }
            val player = playerView.tag as ExoPlayer
            // 先清理之前的播放状态
            player.stop()
            player.clearMediaItems()
            // 设置新的媒体源
            val source = ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(MediaItem.fromUri(video.url))
            player.setMediaSource(source)
            player.prepare()
        },
        onRelease = { playerView ->
            Logger.d { "PlayerView factory released" }
            val player = playerView.tag as ExoPlayer
            // 彻底释放播放器资源
            player.stop()
            player.release()
            playerView.player = null
        }
    )
}

关键调整点说明

  • 复用DataSourceFactory:用remember持有工厂实例,避免每次onReset都创建新对象,减少内存开销。
  • 独立ExoPlayer实例:每个PlayerView绑定独立的ExoPlayer,避免多个视图共享实例导致的资源冲突,同时在onRelease中彻底释放。
  • 设置Pager的item唯一key:确保VerticalPager回收视图时,触发AndroidView的onReset而非重新创建factory,真正实现视图复用。
  • 完善资源释放逻辑:onRelease中解绑PlayerView与播放器,调用release()释放播放器的所有资源,避免内存泄漏。
  • 重置播放状态:onReset中先停止播放、清空媒体项,再设置新的媒体源,避免状态混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 07:43:22