(Deprecated) Why you should call setAdapter(null)
Notice: If you've come here by chance, I want to say thanks for the interest.
And also want to have notice: This post is wrongly representing my message. It (I actually) also uses a bad example to raise the Problem, which cause a misleading discussion.
At the moment, I'm revising my message as well as updating this post. By the meantime, this post is no longer a good place for reference, this is my apologize.
TL;DR: Always call RecyclerView#setAdapter(null)
before your RecyclerView
is going away (in onDestroy/onDestroyView/...
).
1. Problem
In common tutorials about RecyclerView out there, you may see this quite frequently, inside an Activity
's onCreate
or a Fragment
's onCreateView
.
mAdapter = new MyCoolAdapter(); // here mAdapter is a member of enclosing class.
recyclerView.setAdapter(adapter);
It is pretty common step when one initialize his/her screen's RecyclerView
. But I rarely see people telling those "learners" what should them do on the "terminal" point. I mean, does anyone care about tearing down the RecyclerView after all?
One may ask: what's wrong with that? Is Activity
destroying everything after it is destroyed?
Well, for your curiosity: Will it be destroyed?
Let's take a quick look at this question on StackOverflow: RecyclerView
doesn't unregister itself from the adapter on orientation change. I bet everyone can easily find the problem as well as the answer for it. But let me talk a bit more.
Check points to 'The Problem':
- If you have used RecyclerView quite a while, you may know there are couples of useful callback for Adapter:
Adapter#onAttachedToRecyclerView(RecyclerView), Adapter#onDetachedFromRecyclerView(RecyclerView), Adapter#onViewAttachedToWindow(ViewHolder), Adapter#onViewDetachedFromWindow(ViewHolder)
. -
Have you ever debuged to see when they are called? Let me sort them out for you:
onAttachedToRecyclerView
is called when the Adapter is set to RecyclerView, after a call toRecyclerView#setAdapter(Adapter)
orRecyclerView#swapAdapter(Adapter, boolean)
. This is quite obvious.onDetachedFromRecyclerView
, on the other hand, is called when current Adapter if going to be replaced by another Adapter (this another 'Adapter' can be Null). What is the point here: if you don't replace the Adapter, this method will never be called. And what happens if an Adapter is never be "detached" from a RecyclerView? Let's see after I explain about the other couples.onViewAttachedToWindow
is called once RecyclerView or its LayoutManager add a View into RecyclerView (hint: go toRecyclerView
source code and search for the following keywords: dispatchChildAttached).onViewDetachedFromWindow
, on opposite, is called when RecyclerView or its LayoutManager detach a View from current Window.
- What happens when an
Adapter
is not detached from aRecyclerView
? The couple of attach/detach call to an Adapter is tight to another "couple method" of Adapter:registerAdapterDataObserver/unregisterAdapterDataObserver
. In fact,registerAdapterDataObserver
is always called with (right before)onAttachedToRecyclerView
andunregisterAdapterDataObserver
is always called with (right before)onDetachedFromRecyclerView
. In depth,Adapter#registerAdapterDataObserver(AdapterDataObserver)
will add an instance ofAdapterDataObserver
to its observer system, to broadcast the changes toRecycleView
's ecosystem. AndunregisterAdapterDataObserver
will clean it up.RecyclerView
holds an instance ofRecyclerViewDataObserver
which is a non-static inner class that extends AdapterDataObserver (hint: go searching formObserver
inRecyclerView
source code). So here you see the problem?
→ If
onDetachedFromRecyclerView
is not called, which is equivalent to the fact thatunregisterAdapterDataObserver
will not be called, an instance ofRecyclerViewDataObserver
will stay alive inside yourAdapter
as long as thatAdapter
is alive.RecyclerViewDataObserver
, in turn, holds a reference to its enclosingRecyclerView
, which in turn holds a reference to its enclosingContext
. In this case, it is yourActivity
orFragment
's hostContext
(which is 99% anActivity
). So yourActivity
will be held there for quite a long time, longer than you may expect.
2. The Solution
As we have discussed quite long about the Problem, let's see how we can solve it. Since the solution is quite obvious, I would like you to not leave here right now and maybe continue reading to the next paragraph: more motivations for you to always use this solution.
So the solution is:
- In short: remove your Adapter from the RecyclerView as soon as your RecyclerView is going to leave. Which is equivalent to calling:
recyclerView.setAdapter(null)
. - Why this solve the problem? - by calling
setAdapter
with whatever value the Adapter is (as long as it is different to current Adapter), your RecyclerView will always detach current Adapter, which means thatAdapter#onDetachedFromRecyclerView
andAdapter#unregisterAdapterDataObserver
will properly be called. Using a null Adapter will stop the works there. That is!
3. One more thing.
You may say well, it is not bad, but I'm not convinced yet..., let's keep going a bit further.
There is another couple I have discussed above: Adapter#onViewAttachedToWindow
and Adapter#onViewDetachedFromWindow
. Let see 2 (plus 1) screenshots below to see what is the difference between calling setAdapter
to null and not calling it.
Note: my experiment is to setup an Activity with a RecyclerView, and by pressing "Current App Stack" button I can easily make the Activity destroy/recreated. I debug on RecyclerView#onChildAttachedToWindow
and RecyclerView#onChildDetachedFromWindow
which is called along with those 2 methods above, respectively.
- Screenshot 0: after clicking to "App Stack" button, your Activity is destroyed, and by clicking to its "Snapshot" in the stack, you bring it back to life with a "recreation" (savedInstanceState is not null).
- Screenshot 1: not calling setAdapter to null in onDestroy
- Screenshot 2: calling setAdapter to null in onDestroy
You see the difference?
- With proper call to remove the Adapter, current Views on RecyclerView are also properly detached from Window. Otherwise, your Views will not be detached, and I cannot tell where they will be after your Activity is destroyed (or maybe it will not...).
- If you ignore the fact, let see further: the attached Views after your Activity is recreated are all different (see their hashCode in my Screenshot), which means that those older Views is going somewhere else, without a proper "detaching" from the old Window. Where are they? I have no idea.
- Well, I maybe a bit OCD, but not seeing any "onChildDetachedFromWindow" while there is a whole bunch of "onChildAttachedToWindow" make me feel bad. Hope it is not only me out there.
That's all. Happy Coding!