Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView.Adapter onViewDetachedFromWindow never called

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 onViewRecycledin 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.

like image 485
Dokumans Avatar asked Feb 17 '17 12:02

Dokumans


People also ask

How many times onCreateViewHolder called?

By default it have 5.

How many times onBindViewHolder is called?

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.

When onViewRecycled is called?

onViewRecycled() : Once a ViewHolder is successfully recycled, onViewRecycled gets called. This is when we should release resources held by the ViewHolder.


1 Answers

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.

like image 143
eugstman Avatar answered Sep 24 '22 21:09

eugstman