I am working on an Android project where I want to display videos in a RecyclerView using ExoPlayer Media3. To achieve video caching and smooth playback, I've implemented a CacheManager
class, an ExoPlayerManager
class, and a custom ViewHolder
class. However, I'm facing some challenges and would appreciate guidance on how to use ExoPlayer effectively for this purpose.
Here's an overview of my code:
CacheManager Class:
object CacheManager {
private lateinit var cache: SimpleCache
fun initialize(context: Context) {
if (!::cache.isInitialized) {
val cacheDirectory = File(context.cacheDir, "ExoplayerCache")
val maxCacheSize = 100 * 1024 * 1024 // 100 MB cache size
val evictor = LeastRecentlyUsedCacheEvictor(maxCacheSize.toLong())
val databaseProvider: DatabaseProvider = StandaloneDatabaseProvider(context)
cache = SimpleCache(cacheDirectory, evictor, databaseProvider)
}
}
fun getCache(): SimpleCache {
return cache
}
}
ExoPlayerManager Class:
object ExoPlayerManager {
private val players: MutableList<ExoPlayer> = mutableListOf()
fun initializePlayer(context: Context): ExoPlayer {
CacheManager.initialize(context)
val player = players.firstOrNull { it.playbackState == Player.STATE_IDLE }
?: createNewPlayer(context)
players.remove(player)
return player
}
private fun createNewPlayer(context: Context): ExoPlayer {
val player = ExoPlayer.Builder(context).build()
player.setHandleAudioBecomingNoisy(true)
return player
}
fun setMediaItem(
exoPlayer: ExoPlayer,
videoUri: String,
playbackPosition: Long = 0L,
playbackStateListener: Player.Listener
) {
val cacheDataSourceFactory: DataSource.Factory =
CacheDataSource.Factory()
.setCache(CacheManager.getCache())
.setUpstreamDataSourceFactory(
DefaultHttpDataSource.Factory()
.setUserAgent("ExoPlayer"))
val mediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(MediaItem.fromUri(videoUri))
exoPlayer.setMediaSource(mediaSource)
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
exoPlayer.playWhenReady = true
exoPlayer.addListener(playbackStateListener)
exoPlayer.prepare()
exoPlayer.play()
}
fun releasePlayer(holder: HomeTopBannerViewHolder, listItems: BaseModel) {
val player = holder.exoPlayer
listItems.playbackPosition = player?.currentPosition ?: 0L
player?.removeListener(holder.playbackStateListener())
player?.stop()
player?.clearMediaItems()
players.add(player)
}
fun releaseAllPlayers() {
for (player in players) {
player.release()
}
players.clear()
}
}
MyViewHolder Class:
class MyViewHolder(
var binding: LayoutHomeTopBannerBinding, val clickListener: ListActionClickListener?
) : BaseViewHolder(binding.root) {
var exoPlayer: ExoPlayer? = null
fun playbackStateListener(
ivLoader: LottieAnimationView? = null
) = object : Player.Listener{
override fun onPlaybackStateChanged(playbackState: Int) {
when (playbackState) {
Player.STATE_ENDED -> {
// Handle playback ended
}
Player.STATE_READY -> {
ivLoader?.visibility = View.GONE
}
Player.STATE_BUFFERING -> {
ivLoader?.visibility = View.VISIBLE
}
}
}
}
override fun onBind(item: BaseModel, position: Int) {
exoPlayer = ExoPlayerManager.initializePlayer(binding.root.context)
val screenHeight =
binding.root.context.findActivity()?.getRealScreenSize()?.second.getSafe() //height
binding.apply {
itemView.apply {
videoView.layoutParams.height = screenHeight * 3 / 5
videoView.requestLayout()
ivLoader.layoutParams.height = screenHeight * 3 / 5
ivLoader.requestLayout()
}
itemView.setOnClickListener {
clickListener?.onItemClick(ListActionClickType.OnItemClick(item, position))
}
videoView.player = exoPlayer
ExoPlayerManager.setMediaItem(
exoPlayer!!,
"Any Url",
item.playbackPosition,
playbackStateListener(ivLoader)
)
}
}
}
I have a few questions regarding this setup:
How can I efficiently cache videos using the CacheManager class and ensure that they are reused in the RecyclerView?
Is the way I'm initializing and using ExoPlayer in the ExoPlayerManager class correct, especially in the setMediaItem method?
Are there any potential issues or improvements that I should be aware of when working with ExoPlayer in a RecyclerView context?
Any guidance, code samples, or suggestions on how to optimize this implementation would be greatly appreciated. Thank you!
In addressing challenges related to RecyclerView video playback using ExoPlayer Media3, I have to enhance my ExoplayerManager class like this:
Few methods are specific to my needs and the methods that work for me are:
buildCacheDataSourceFactory(), getDownloadCache()
Here are my Extension functions of Recyclerview that help me to reuse the same code for playing and resuming the video on scrolling
Hopefully this answer will be helpful for you to use cache videos in recyclerview, Thanks