Lists & Preloading
This document provides tips and guidance over the implementation of scrollable lists containing videos, for example a TikTok-like feed in a pager component.
Use playlists
The first and most important point is that Playlist
s should be used. When a collection of videos
is wrapped in a playlist:
- video content is cached
- videos are preloaded based on heuristic depending on how the playlist is accessed
- for paged playlists, new pages are automatically opened
This ensures the best performance during fast swiping, so it is highly recommended over lists of videos. Please check the playlist documentation for more information on how to create, retreive and manage playlists.
Use PlayersManager
Just like a Playlist
manages a set of video objects, our PlayersManager
helps managing players instances efficiently. The manager will:
- (pre) allocate players
- enable video preloading
- retreive videos from the playlist and apply them to each player
This means that all you have to do is retreive the player and start playing. You use:
PlayersManager.get()
to ask for a new playerPlayersManager.release(DefaultPlayer)
to return it to the poolPlayersManager.playbackQuality
to coordinate the playback (and preload!) quality across all players
Note that it is very important to destroy the players manager once you are done with it.
val playlist = ...
val manager = PlayersManager(context, playlist)
// Later...
manager.destroy()
Listen to playlist changes
Playlists are smart objects that hold dynamic data. The underlying dataset can change for several
reasons, for example if new videos are uploaded, old videos are deleted, or if a CustomPlaylist
is modified using add/remove functionality.
In all these cases, it is important to observe changes in the dataset and, for example, notify your RecyclerView.Adapter
about it:
class Adapter(lifecycle: LifecycleOwner, playlist: Playlist): RecyclerView.Adapter<Item>() {
init {
playlist.observeIn(lifecycle, object : Playlist.Observer {
override fun onVideoInserted(id: String, index: Int) = notifyItemInserted(index)
override fun onVideoChanged(id: String, index: Int) = notifyItemChanged(index, id)
override fun onVideoRemoved(id: String, index: Int) = notifyItemRemoved(index)
override fun onVideoMoved(id: String, fromIndex: Int, toIndex: Int) {
notifyItemMoved(fromIndex, toIndex)
}
})
}
override fun getItemCount(): Int {
return playlist.size
}
}
Avoid change animations
Note how in the example above, we have called adapter.notifyItemChanged(index, payload)
, passing a payload object.
This is important because it tells the RecyclerView
to avoid 'change' animations.
In these animations, the recycler will use two separate views and bind them to the same data, while
animating them in / out. This does not play very well with the player, who needs to allocate resources
that shouldn't be bound to multiple views at the same time. We recommend to always use the
adapter.notifyItemChanged(index, payload)
signature and passing a non-null payload (like the video id).
Autoplay videos on scroll
The RecyclerView
scroll callback can be used to autoplay videos as soon as they are scrolled in.
You are free to implement your own logic, but a simple implementation could be this:
private val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
maybePlay(recyclerView)
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
maybePlay(recyclerView) // Important for first layout
}
private fun maybePlay(recyclerView: RecyclerView) {
if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE) {
val manager = recyclerView.layoutManager!! as LinearLayoutManager
val position = manager.findFirstCompletelyVisibleItemPosition()
if (position >= 0) {
val holder = recyclerView.findViewHolderForAdapterPosition(position) as? Item
holder?.play()
}
}
}
}
This listener can be registered in onAttachedToRecyclerView
and unregistered in onDetachedFromRecyclerView
.