Video Player's AutoPlay Framework Design in RecyclerView

When I reviewed the notes, I found that this article was not uploaded to the blog. Although the code implementation is outdated, the idea has been used all the time, so today I simply sorted the content and sent it to the blog.

Auto-playing of the video is a common application scenario, which involves sliding distance monitoring of lists, lifecycle perception of pages, and changes in video state. This article describes an automatic playback architecture design for RecyclerView component.

Design

Traditional Scheme

Common scenario: Get PlayerId in Item, instantiate Player and play it.

The easiest way to think of this is to put a Player in the layout of each item in the RecyclerView, and listen to the RecyclerView to determine the currently visible item, then instantiate the each Player.

The problem with this solution is that when sliding the list, the Player is constantly instantiated, causing additional overhead.

There are also proposals to use a player singleton, calling addView() and removeView() during the sliding process to handle the player. Although this solution solves the above-mentioned problem of constant re-instantiation, there are also specific scenarios. The next unsuitable problem, such as the vertical sliding RecyclerView nested with a horizontal sliding RecyclerView, two RecyclerView have automatic playback player. In such a nested scenario, the player logic processing is more complicated.

My Scheme

Because the above scenario is very likely to be encountered in the actual development process, in order to reduce the repetitive coding, a set of automatic playback structure suitable for RecyclerView is designed, which can solve the player in the case of multi-level nesting. The logic code is written and the idea of “Self-Management” is implemented, which also makes the trivial processing of the player’s lifecycle perception more cohesive.

Usually, we have a RecyclerView in an Activity, in which there are various types of Item, such as pictures, videos, and other types, only the video needs to support automatic playback.

Here we abstract the player into an interface IPlayer as shown below:

1
2
3
4
5
6
interface IPlayer {
fun setUp(url: String?) // Set url to video
fun getUrl(): String? // Get url of video
fun autoPlay() // just method of play
fun autoRemove() // just method of pause
}

Modify the corresponding player, such as MyPlayer, to inherit the interface. Note that its autoRemove() function is used to let the player remove its own parent layout and pause itself. The reference implementation is as follows:

1
2
3
4
5
6
override fun autoRemove() {
if (this.parent != null) {
(this.parent as ViewGroup).removeView(this)
this.pause()
}
}

At this point, we have a universal player interface, as long as you follow this interface, you can change the implementation.

Then, you need a container of video. When the item has not been played, the video is filled by the container of the video. We assume here as IVideoContainer, which encapsulates some trivial logic such as null processing, mainly providing two externally. Methods, as follows:

1
2
3
4
interface IVideoContainer{
fun addVideo(player: IPlayer)
fun removeVideo(player: IPlayer)
}

Next, we also need an Item interface to indicate that the Item supports autoplay, as follows:

1
2
3
4
5
interface IAutoPlayItem {
fun bindPlayer(player:IPlayer)
fun autoPlay()
fun autoPause()
}

The item that inherits the interface can be played automatically.

Finally, we also need a play help class to help us monitor the distance change of RecyclerView. The key functions are as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fun setListener() {
_recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> {
checkVisibleItem()
}
}
}
})
}


private fun canAutoPlay(view: View?, player: IPlayer?): Boolean {
if (view == null || player == null) {
return false
}
if (view is IAutoPlayItem) {
view.bindPlayer(player)
return true
}
return false
}

At this point, the core function class of the scheme has been explained, and then the Lifecycle is introduced to the Player, that is, the lifecycle perception is realized, and the single-player processing of the core playback part of the Player can realize the cross-page single player architecture.

More

Although this article is for RecyclerView, because it is only coupled with the listener helper class, it can be reused in other layouts, and the corresponding helper classes can be reused, such as ViewPager.

The above example code only judges the playable state of the first visible Item in the page. In the actual scene, the online example can be used to make a percentage judgment on each visible Item in the page, and the priority processing is performed.

Summary

Compared with the traditional scheme, the advantage of the scheme described in this paper is that, when the page layout is complicated, the framework does not have to be modified for complex situations, and the logical processing of the complex page layout is sunk to the automatically played Item interface. At the same time, it supports the fast resume function after the player is skipped in the multi-type layout. And the overall design removes the player logic from the page. For each page, you only need to add Observer of Lifecycle and give RecyclerView to the help class, so the cost of access is very small.