Skip to main content
Version: Android SDK v1.3.0

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 Playlists 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 player
  • PlayersManager.release(DefaultPlayer) to return it to the pool
  • PlayersManager.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.