So, this might be a bit of an overkill solution, but our team was getting pretty tired of having to worry about every single RecyclerView adapter leaking.
Here's an abstract RecyclerView.Adapter subclass that takes care of the leak issue once and for all.
abstract class AbstractRecyclerViewAdapter<VH : RecyclerView.ViewHolder>(fragment: Fragment) :
RecyclerView.Adapter<VH>() {
private val fragmentRef = WeakReference(fragment)
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
setupLifecycleObserver(recyclerView)
}
private fun setupLifecycleObserver(recyclerView: RecyclerView) {
val fragment = fragmentRef.get() ?: return
val weakThis = WeakReference(this)
val weakRecyclerView = WeakReference(recyclerView)
// Observe the fragment's lifecycle events in order
// to set the Recyclerview's Adapter when the Fragment is resumed
// and unset it when the Fragment is destroyed.
fragment.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
val actualRecyclerView = weakRecyclerView.get() ?: return
when (event.targetState) {
Lifecycle.State.DESTROYED -> actualRecyclerView.adapter = null
Lifecycle.State.RESUMED -> {
val self = weakThis.get() ?: return
if (actualRecyclerView.adapter != self) {
actualRecyclerView.adapter = self
}
}
else -> {
}
}
}
})
}
}
Basically the Adapter observes the lifecycle events of the Fragment that contains it and takes care of setting and unsetting itself as the RecyclerView's adapter.
All you need to do is subclass AbstractRecyclerViewAdapter
in your own adapters, and provide the container fragment when you construct them.
Usage:
Your adapter
class MyAdapter(fragment: Fragment): AbstractRecyclerViewAdapter<MyViewHolder>(fragment) {
// Your usual adapter code...
}
Your fragment
class MyFragment : Fragment {
// Instantiate with the fragment.
private val myAdapter = MyAdapter(this)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set the RecyclerView adapter as you would normally.
myRecyclerView.adapter = myAdapter
}
}