"首帧黑屏三秒,我就默认这播放器凉了。"
免费源给人的印象常常是"随缘播放"。我希望至少能做到:当用户点播放按钮时,播放器在台前台后都要忙起来:晚高峰源慢?先缓存播放 URL;剧集多?先探测可播项;怕跳转失败?带上预加载策略。我们来看看我具体做了什么。
我给自己定了几个任务:
- 点击播放那一刻就锁定剧集、API 信息,避免重复请求;
- 如果是多源,快速检查可播放 URL,失败就自动切换;
- 在 PlayerActivity 里保证首帧尽快出现------包括预加载与缓存 fallback。
为达成这些目标,需要在播放器入口和 PlayerActivity 两侧动手术。
搜索阶段:提前确认可播 URL
当用户在 SearchActivity 点击剧集时,我会即时拉取详情,解析 episode,并且根据源类型决定展示方式。关键是 handleVideoSelection() 里,在确定要播放之前,就把 detail.vodId / sourceCode / apiUrl 携带在 Intent 里。
同时,如果剧集只有一集或电影,就直接决定 firstEpisode,免得进入 PlayerActivity 再去猜。
kotlin
val intent = Intent(this, PlayerActivity::class.java).apply {
putExtra("VIDEO_ID", detail.vodId)
putExtra("VIDEO_TITLE", detail.vodName)
putExtra("EPISODE_NAME", firstEpisode.name)
putExtra("EPISODE_URL", firstEpisode.url)
putExtra("EPISODE_INDEX", 0)
putExtra("SOURCE_CODE", detail.sourceCode)
putExtra("SOURCE_NAME", detail.sourceName)
putExtra("API_URL", detail.apiUrl)
}
这样做的好处是,PlayerActivity 一启动就能知道该请求哪个 API、哪个源,不需要再回头问 SearchActivity。
PlayerActivity:预加载和降级策略
在 PlayerActivity 里,最关键的几个动作:
- 首帧优化 :在
onCreate中就开始准备 ExoPlayer,同时加载海报/背景图,界面上至少有东西先顶着。 - 自动切源 :如果当前源的 episode URL 播放失败,就调用
VideoDetailViewModel再拉一次详情找其它可播项,或者直接提示"本源无效"。 - 缓存回退 :
VideoDetailCache会存之前看过的详情,如果源暂时挂掉,也能用旧列表让用户选择,再尝试播放。
对于预加载,我采用了"先准备 MediaItem 再 attach 到播放器"的方式:
kotlin
private fun preparePlayer(episodeUrl: String) {
val mediaItem = MediaItem.Builder()
.setUri(episodeUrl)
.setMimeType(MimeTypes.APPLICATION_M3U8)
.build()
exoPlayer.setMediaItem(mediaItem, /* startPosition= */ 0)
exoPlayer.prepare() // 先准备,等 UI ready 再 playWhenReady = true
}
这一步在 onCreate 就会调用,所以等 UI 渲染完再 play() 时,首帧更快出现。实际体验上,晚高峰也能把"黑屏状态"压缩到 1 秒以内。
夜间切源:别让坏源拖累体验
晚上的问题主要是某些源响应慢甚至直接挂。我在 VideoRepository 的流式搜索里,已经把每个源的结果分批返回;在 PlayerActivity 里,只要某个 episode 播放失败,就会回调 ViewModel 去拿其他源:
kotlin
viewModel.videoDetail.observe(this) { detail ->
// detail.episodes 里包含当前源可播记录
}
再结合 CommonEpisodesBottomSheet,用户可以重新选一集或者换源,避免"完全没响应"的尴尬。
现在的体验怎么样?
- 点击播放后 UI 立刻变化,海报、背景先占位,不再是灰屏;
- 首帧出现时间更稳定,晚高峰也能在 1~2 秒内看到画面;
- 源挂了也会提示,用户可以换源或重试,而不是直接崩;
- 缓存回退让"恰好源挂掉"的场景还能继续找上一条剧集。
想问问你
- 你遇到过最难忍的播放器首帧问题是什么?
- 如果可以自定义缓冲策略,你希望哪些参数(起播时间、最大重试次数)开放给用户?
- 除了自动切源,你还希望播放器在失败时做什么?比如一键反馈?欢迎留言讨论。
免费看剧本来就静不下心,再让首帧黑屏,只会让人更想卸载。希望这套缓冲和预加载策略,也能帮你在自己的项目里少一点"随缘",多一点可控。欢迎留言分享你在播放层的优化手法,我们继续折腾。