This gist snippet is Public Domain.
-
-
Save keima/1b8cda30aec8cd50fec7743d2ccfa777 to your computer and use it in GitHub Desktop.
import android.os.Bundle | |
import android.util.Log | |
import android.view.ViewGroup | |
import android.widget.TextView | |
import androidx.appcompat.app.AppCompatActivity | |
import androidx.databinding.DataBindingUtil | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.LifecycleObserver | |
import androidx.lifecycle.OnLifecycleEvent | |
import app.keima.android.recyclerviewsandbox.databinding.ActivityMainBinding | |
class MainActivity : AppCompatActivity() { | |
private lateinit var binding: ActivityMainBinding | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
binding = DataBindingUtil.setContentView(this, R.layout.activity_main) | |
binding.recyclerView.apply { | |
adapter = MyAdapter( | |
arrayOf( | |
"A", "B", "C", "D", | |
"A", "B", "C", "D", | |
"A", "B", "C", "D", | |
"A", "B", "C", "D" | |
) | |
) | |
} | |
} | |
} | |
class MyAdapter(private val dataset: Array<String>) : | |
LifecycleRecyclerAdapter<MyAdapter.MyViewHolder>() { | |
class MyViewHolder(private val textView: TextView) : LifecycleViewHolder(textView) { | |
private val observer = MyObserver() | |
init { | |
lifecycle.addObserver(observer) | |
} | |
fun bindData(data: String) { | |
textView.text = data | |
observer.data = data | |
} | |
} | |
class MyObserver() : LifecycleObserver { | |
var data: String = "?" | |
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE) | |
fun onCreate() { | |
Log.d("MyObserver", "appear: $data") | |
} | |
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) | |
fun onDestroy() { | |
Log.d("MyObserver", "disappear: $data") | |
} | |
} | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { | |
return MyViewHolder(TextView(parent.context).apply { | |
setPadding(8, 40, 8, 40) | |
}) | |
} | |
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { | |
holder.bindData("${dataset[position]} $position") | |
} | |
override fun getItemCount() = dataset.size | |
} |
import androidx.recyclerview.widget.RecyclerView | |
abstract class LifecycleRecyclerAdapter<T : LifecycleViewHolder> : RecyclerView.Adapter<T>() { | |
override fun onViewAttachedToWindow(holder: T) { | |
super.onViewAttachedToWindow(holder) | |
holder.onAppear() | |
} | |
override fun onViewDetachedFromWindow(holder: T) { | |
super.onViewDetachedFromWindow(holder) | |
holder.onDisappear() | |
} | |
} |
import android.view.View | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.LifecycleOwner | |
import androidx.lifecycle.LifecycleRegistry | |
import androidx.recyclerview.widget.RecyclerView | |
abstract class LifecycleViewHolder(itemView: View) : | |
RecyclerView.ViewHolder(itemView), LifecycleOwner { | |
private val lifecycleRegistry = LifecycleRegistry(this) | |
init { | |
lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED | |
} | |
fun onAppear() { | |
lifecycleRegistry.currentState = Lifecycle.State.CREATED | |
} | |
fun onDisappear() { | |
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED | |
} | |
override fun getLifecycle(): Lifecycle { | |
return lifecycleRegistry | |
} | |
} |
@jonathands, this is my repo you can see there https://github.com/abhinavjiit/baseRecyclerview/blob/master/app/src/main/java/com/example/pristencare/adapter/RecyclerViewImageAdapter.kt
Amazing! I will have a look at it now, try to implement and run Leakcanary! Thanks a lot!!
if you got something wrong or any memory leak, plz let me know as well
Sure! I am implementing it now. Might take a lil bit because I want to fully understand the logic behind it but I should be able to fully test it today :)
@jonathands, this is my repo you can see there https://github.com/abhinavjiit/baseRecyclerview/blob/master/app/src/main/java/com/example/pristencare/adapter/RecyclerViewImageAdapter.kt
could you fix your mention so I don't get the notifications?
@abhinavjiit It's working like a charm! No memory leaks, no excess of objects allocated... It's just perfectly working! Well done!
@abhinavjiit It's working like a charm! No memory leaks, no excess of objects allocated... It's just perfectly working! Well done!
@jonathansds Cool bro.
@abhinavjiit
Hi! - I think there is possibly something wrong: RecyclerView.getChildCount
and RecyclerView.getChildAt
only return visible items.
My implementation
class SimpleLifeCycleOwnerImpl : SimpleLifeCycleOwner {
private var lifecycleRegistry: LifecycleRegistry? = null
override fun getLifecycle() =
lifecycleRegistry ?: run {
initialize()
lifecycleRegistry!!
}
override fun initialize() {
// The object can be revived, create a new lifecycle
lifecycleRegistry?.run {
if (currentState != Lifecycle.State.DESTROYED)
throw IllegalStateException("can be revived only after destroyed, get $currentState")
}
lifecycleRegistry = LifecycleRegistry(this)
}
override fun handleLifecycleEvent(event: Lifecycle.Event) {
lifecycleRegistry!!.handleLifecycleEvent(event)
}
}
// kotlin cannot inherit from type parameter like C++, we must use this class as base
// otherwise we need to copy the code for each adapter
abstract class LifecycleAdapter<VH : RecyclerView.ViewHolder>(lifecycleOwner: LifecycleOwner) :
RecyclerView.Adapter<VH>() {
private var recyclerView: RecyclerView? = null
init {
val observer = object : LifecycleEventObserver {
private fun visibleChildApply(block: SimpleLifeCycleOwner.() -> Unit) =
recyclerView?.run {
for (i in 0 until childCount)
getChildAt(i)?.let {
(getChildViewHolder(it) as? SimpleLifeCycleOwner)?.block()
}
} ?: Unit
private fun allChildApply(block: SimpleLifeCycleOwner.() -> Unit) =
recyclerView?.run {
for (i in 0 until itemCount)
(findViewHolderForAdapterPosition(i) as? SimpleLifeCycleOwner)?.block()
} ?: Unit
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) =
when (event) {
Lifecycle.Event.ON_DESTROY -> allChildApply { handleLifecycleEvent(event) }
else -> visibleChildApply { handleLifecycleEvent(event) }
}
}
lifecycleOwner.lifecycle.addObserver(observer)
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
if (this.recyclerView != null)
throw IllegalStateException("can be attached to only one RecyclerView")
this.recyclerView = recyclerView
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
throw IllegalStateException("must not be detached")
}
override fun onViewAttachedToWindow(holder: VH) =
(holder as? SimpleLifeCycleOwner)?.run {
handleLifecycleEvent(Lifecycle.Event.ON_START)
handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
} ?: Unit
override fun onViewDetachedFromWindow(holder: VH) =
(holder as? SimpleLifeCycleOwner)?.run {
handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
handleLifecycleEvent(Lifecycle.Event.ON_STOP)
} ?: Unit
override fun onBindViewHolder(holder: VH, position: Int) =
(holder as? SimpleLifeCycleOwner)?.run {
initialize()
handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
// logical views are created here
} :? Unit
override fun onViewRecycled(holder: VH) =
(holder as? SimpleLifeCycleOwner)?.run {
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
} ?: Unit
}
It will cause memory leak, when RecyclerView detach from window, the onViewDetachedFromWindow callback will not invoked.
if you got something wrong or any memory leak, plz let me know as well