What's the way to go if I want my ViewHolders in an RecyclerView to clean up internal state (in this case unregister from an EventBus and clean up Rx-Subscriptions)?
I thought that the methods onViewDetachedFromWindow
or onViewRecycled
in the adapter is the callback where I can cleanup resources (as described in the API), but this method is never called when I change from the Activity
with the RecyclerView
to another Activity
.
Only onViewAttachedToWindow
is called when the activity is entered and i can see my items.
My adapter looks like this:
public class Adapter extends RecyclerView.Adapter<MyViewHolder>
{
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return factory.getViewholder(parent, viewType, this);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
MyItem item = items.get(position);
holder.bind(item);
}
@Override
public void onViewDetachedFromWindow(MyViewHolder holder)
{
holder.viewDetached();
super.onViewDetachedFromWindow(holder);
}
@Override
public void onViewAttachedToWindow(MyViewHolder holder)
{
super.onViewAttachedToWindow(holder);
holder.viewAttached();
}
@Override
public void onViewRecycled(MyViewHolder holder)
{
super.onViewRecycled(holder);
holder.viewRecycled();
}
@Override
public int getItemCount() {
return items.size();
}
@Override
public int getItemViewType(int position)
{
return items.get(position).getType(this.factory);
}
}
The Activity
holding the RecyclerView
:
public class MyActivity extends AppCompatActivity
{
protected void setupView()
{
//called in onCreate
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
this.adapter = new Adapter(new ItemViewHolderFactory());
}
@Override
public void setItems(List<MyItem> items)
{
//items are loaded async
this.adapter.setItems(items);
this.recyclerView.setAdapter(this.adapter);
this.recyclerView.scheduleLayoutAnimation();
}
}
The ViewHolder looks like this
public class MyViewHolder extends RecyclerView.ViewHolder
{
public void bind(MyItem item)
{
//set initial data
}
public void viewAttached()
{
registerToEventBus();
loadDataAsync(); // here an rx operation is scheduled and a subscription is hold
}
public void viewDetached()
{
unregisterFromEventBus();
cancelAsyncOperationAndCleanSubscription();
}
}
Thanks for any advices.
EDIT
I tried to override onDetachedFromRecyclerView
in the adapter, but this method is also not called.
By default it have 5.
1 Answer. Show activity on this post. Yes it is perfectly normal for a RecyclerView to call onBindViewHolder() multiple times. A RecyclerView only creates minimum number of Views needed to fill the screen.
onViewRecycled() : Once a ViewHolder is successfully recycled, onViewRecycled gets called. This is when we should release resources held by the ViewHolder.
I run into the same problem:
As already mentioned you can set the adapter null in the onPause (Activity, Fragment) or onDetachFromWindow (Activity, Fragment)
recyclerview.setAdapter(null)
Then you get viewDetachedFromWindow(...) where you can release your internal states and subscriptions. I would setup your subscriptions in on bind, make sure before every bind call you relase old subscriptions as a view can be recycled.
Another possibility is to inflate a custom view instead of only a layout in your factory. Then you can make the cleanup in the custom view onDetachFromWindow(). You get the onDetachedFromWindow also without setting the adapter to null.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With