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

ExoPlayer结合LazyColumn实现Instagram Reels效果优化求助

ExoPlayer + LazyColumn 实现Reels滚动卡顿优化方案

1. PlayerView复用与状态管理优化

复用PlayerView的核心是避免重组时重复初始化,用remember缓存实例,并配合DisposableEffect处理媒体项切换的生命周期:

LazyColumn {
    items(videos, key = { it.id }) { video ->
        val playerView = remember {
            PlayerView(context).apply {
                player = exoPlayerSingleton
                useController = false
                resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL
            }
        }

        DisposableEffect(video) {
            // 切换当前媒体项
            exoPlayerSingleton.setMediaItem(MediaItem.fromUri(video.uri))
            exoPlayerSingleton.prepare()
            exoPlayerSingleton.play()

            onDispose {
                exoPlayerSingleton.pause()
                exoPlayerSingleton.clearMediaItems() // 释放当前媒体资源
            }
        }

        playerView
    }
}
  • 必须给items设置唯一key,确保LazyColumn能精准复用列表项
  • 用remember缓存PlayerView,仅首次创建时初始化

2. 预加载策略调整

一次性预加载所有媒体项会导致内存过载,改为滚动时动态预加载相邻项:

val listState = rememberLazyListState()

LaunchedEffect(listState.firstVisibleItemIndex) {
    val currentIndex = listState.firstVisibleItemIndex
    // 仅预加载当前项的前后各1个视频
    val preloadRange = (currentIndex -1)..(currentIndex +1)
    val targetIndices = preloadRange.filter { it in videos.indices }

    // 添加需要预加载的媒体项
    targetIndices.forEach { index ->
        val mediaItem = MediaItem.fromUri(videos[index].uri)
        if (!exoPlayerSingleton.mediaItems.any { it.mediaId == video.id }) {
            exoPlayerSingleton.addMediaItem(mediaItem)
        }
    }

    // 移除超出预加载范围的旧媒体项
    val removeIndices = exoPlayerSingleton.mediaItems.indices.filter {
        it < currentIndex -2 || it > currentIndex +2
    }
    removeIndices.reversed().forEach { exoPlayerSingleton.removeMediaItem(it) }
}

3. ExoPlayer性能参数调优

调整缓冲策略和渲染配置,减少加载延迟与资源占用:

// 自定义缓冲配置,降低首次加载门槛
val loadControl = DefaultLoadControl.Builder()
    .setBufferDurationsMs(
        1500,  // 最小缓冲时长
        4000,  // 最大缓冲时长
        800,   // 播放前需缓冲的时长
        800    // 缓冲不足时的重试间隔
    )
    .build()

// 初始化单例Player时应用配置
val exoPlayerSingleton = remember {
    ExoPlayer.Builder(context)
        .setLoadControl(loadControl)
        .setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
        .build()
}
  • 关闭不需要的监听(如videoFrameMetadataListener),减少主线程回调
  • 若设备硬件加速兼容性差,可尝试禁用:playerView.useHardwareAcceleration = false

4. LazyColumn滚动优化

  • 禁用不必要的过度滚动效果,减少UI绘制开销:
LazyColumn(
    state = listState,
    modifier = Modifier.overScroll(OverScrollBehavior.None)
) {
    // ... 列表项逻辑
}
  • 避免在列表项中执行复杂计算或IO操作,所有数据预处理提前完成

5. 资源释放机制完善

确保页面销毁时彻底释放Player资源,避免内存泄漏:

DisposableEffect(Unit) {
    onDispose {
        exoPlayerSingleton.stop()
        exoPlayerSingleton.release()
    }
}
  • 给PlayerView添加生命周期监听,确保页面不可见时暂停播放:
playerView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
    override fun onViewAttachedToWindow(v: View?) {
        exoPlayerSingleton.play()
    }

    override fun onViewDetachedFromWindow(v: View?) {
        exoPlayerSingleton.pause()
    }
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:37:03