Search code examples
androidkotlinandroid-recyclerviewandroid-mediaplayerandroid-lifecycle

How / when to release the resources acquired by viewHolder in a recyclerView?


To be simple, I have a recyclerView which has viewHolders holding MediaPlayer instances. When users close that activity, I want to release the resources aquired by MediaPlayer instance. But unlike onCreateViewHolder, I couldn't find any onDestroyViewHolder methods. My viewHolder implementation looks like this:

class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    private val mMediaPlayer: MediaPlayer? = null
    
    internal fun onBind(model: Model) {
        if(model.contentType == "Audio") {
            mMediaPlayer = MediaPlayer()
            mMediaPlayer.setOnPreparedListener(mPreparedListener);
            mMediaPlayer.setOnCompletionListener(mCompletionListener);
            mMediaPlayer.setDataSource(mContext, uri, null)
            mMediaPlayer.prepareAsync()
            // do other stuff

        } else if(mMediaPlayer != null) {
            mMediaPlayer.reset()
            mMediaPlayer.release()
            mMediaPlayer = null
        }
    }
}

I know the Adapter class has onViewDetachedFromWindow method. But I am not sure whether that can fit here. Can someone tell me a solution for this?


Solution

  • You should create method to releaseResources in your viewHolder and call it from onViewRecycled which is overriden in your recycler adapter

        override fun onViewRecycled(holder: MediaViewHolder) {
            super.onViewRecycled(holder)
            val videoItem = mediaItems.getOrNull(holder.bindingAdapterPosition)
            holder.releasePlayer(videoItem)
        }
    

    in order to get this method called each time your fragment goes to stop state you should set recyclerview adapter to null

      override fun onStop() {
            super.onStop()
            currentPage = binding.pager.currentItem
            binding.pager.adapter = null
      }
    

    this will cause onViewRecycled to be called on all viewHolders

    and you should set adapter back on recyclerview in onStart()

    override fun onStart() {
        super.onStart()
        if (binding.pager.adapter == null) {
            binding.pager.adapter = adapter
            binding.pager.setCurrentItem(currentPage, false)
        }
    }
    

    this example is on ViewPager2 but this works with RecyclerView also.