I'm experiencing some unexpected issues with Items and cleaning up references when items should be unbound. But, maybe I'm not understanding how the unbind function is supposed to work.
If I set up listeners or RxJava subscriptions in bind(viewHolder, position) of a custom Item class and I exit the activity which holds reference to the adapter containing these items, shouldn't unbind(holder) be called on all items?
Is there something specific I have to call before the activity/fragment is stopped?
In my particular case, unbind(holder) is never called for any of the items in my adapter.
I'm happy to create a bug ticket if this turns out to be a bug.
unbind(holder) is called whenever a ViewHolder is recycled (called from RecyclerView.Adapter.onViewRecycled method).
Activities/Fragments do not explicitly alert RecyclerView adapters about lifecycle changes.
There is two things you can do here depending on your use case. One of them is to override the onViewAttachedToWindow and onViewDetachedFromWindow functions in Item. These are driven from the corresponding functions from the recyclerview adapter. But you will NOT get an
onViewDetachedFromWindow event when the activity/fragment is stopped or destroyed.
What it sounds like to me is that your Items need to implement LifecycleObserver. And if your RecyclerView is used within an Activity it should be really simple to use:
class MyItem() : Item(), LifecycleObserver {
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
(viewHolder.itemView.context as AppCompatActivity).lifecycle.addObserver(this)
}
override fun unbind(viewHolder: GroupieViewHolder) {
super.unbind(viewHolder)
(viewHolder.itemView.context as AppCompatActivity).lifecycle.removeObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
//perform onStopLogic
}
}
Things are a little bit tricker if you want to bind it to a Fragment lifecycle. Since the context of the viewHolder.itemView will ultimately be an Activity one. In that case you'd need to pass the reference of the Fragment as a LifecycleOwner to your items.
Does this help?
@TylerMcCraw another suggestion I have - if your only issue here is clearing RxJava subscriptions then perhaps the simplest thing would be to pass a CompositeDisposable to your Items which you then clear from your Activity/Fragment/ViewModel
@ValCanBuild Thanks so much for the suggestions.
I get worried about attaching Lifecycle to RecyclerView items just because I want to avoid memory leaks in all possible cases.
Also, I was hoping that I could keep my implementation of the Rx CompositeDisposable in place within the Item since the unbind does work in clearing the subscription for that specific Item as the RecyclerView is scrolled and Items are recycled.
My hope was that there would be some way to tell the GroupieAdapter to unbind all items in onStop of Activity/Fragment and maybe rebind them when the Activity/Fragment is restarted.
There's nothing like that currently, is there?
I suppose I could just call clear() on the adapter and then re-add the items when the Activity/Fragment is restarted. But this would more than likely cause some unwanted animations and would cause the user to lose their scroll position within the RecyclerView.
@TylerMcCraw calling clear() will not solve your issue - it will not call unbind on the items.
If you don't want to use the Lifecycle functionality in your item then the only thing i could think of is creating your own GroupAdapter subclass with an onStop method you call manually which then in turns notifies each item in the adapter about the state change.
But, honestly, using the Lifecycle is just a better approach imo. I've got code in production using this.
In my example code I show you where to bind/unbind from it. Plus, your adapter and items in it are tied to the life of your encapsulating Activity/Fragment anyway - it can't outlive unless you really go out of your way to do it.
Ok 馃憤
I'll give the LifecycleObserver option a shot and report back just so other folks can see a resolution.
But, feel free to close the ticket.
Really appreciate your help!
Just want to mention this to anyone coming here:
The LifecyclerObserver + onStop option worked for me. It allowed me to properly clean up any Rx observables that remained subscribed when the Activity/Fragment was stopped.
I tested this implementation and it's working as expected.
@TylerMcCraw Can you share some part of your implementation? Because I have a increase of MemoryLeak in my app since I am using Groupie