(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:
onAttachedToRecyclerViewis 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.onViewAttachedToWindowis called once RecyclerView or its LayoutManager add a View into RecyclerView (hint: go toRecyclerViewsource 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
Adapteris 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,registerAdapterDataObserveris always called with (right before)onAttachedToRecyclerViewandunregisterAdapterDataObserveris always called with (right before)onDetachedFromRecyclerView. In depth,Adapter#registerAdapterDataObserver(AdapterDataObserver)will add an instance ofAdapterDataObserverto its observer system, to broadcast the changes toRecycleView's ecosystem. AndunregisterAdapterDataObserverwill clean it up.RecyclerViewholds an instance ofRecyclerViewDataObserverwhich is a non-static inner class that extends AdapterDataObserver (hint: go searching formObserverinRecyclerViewsource code). So here you see the problem?
→ If
onDetachedFromRecyclerViewis not called, which is equivalent to the fact thatunregisterAdapterDataObserverwill not be called, an instance ofRecyclerViewDataObserverwill stay alive inside yourAdapteras long as thatAdapteris alive.RecyclerViewDataObserver, in turn, holds a reference to its enclosingRecyclerView, which in turn holds a reference to its enclosingContext. In this case, it is yourActivityorFragment's hostContext(which is 99% anActivity). So yourActivitywill 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
setAdapterwith whatever value the Adapter is (as long as it is different to current Adapter), your RecyclerView will always detach current Adapter, which means thatAdapter#onDetachedFromRecyclerViewandAdapter#unregisterAdapterDataObserverwill 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!