preface
In the development of the project, memory leakage occurred when Fragment used RecyclerView. Record the cause and solution of the problem.
Cause analysis,
Create a RecyclerView Adapter in your Fragment. Create a RecyclerView Adapter in your Fragment. Create a RecyclerView Adapter in your Fragment.
private lateinit var mAdapter: BaseAdapter<String>
private lateinit var mRecyclerView: RecyclerView
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup? , savedInstanceState: Bundle? ): View? {
_binding = DataBindingUtil.inflate(inflater, layoutId, container, false)
mAdapter = BaseAdapter<String>()
mRecyclerView = _binding.rvList
mRecyclerView.adapter = mAdapter
return _binding.root
}
Copy the code
RecyclerView code:
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
public void setAdapter(@Nullable Adapter adapter) {...//ignore
setAdapterInternal(adapter, false.true); .//ignore
}
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {...//ignore
mAdapter = adapter;
if(adapter ! =null) {
// Register RecyclerView data observation
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this); }}Copy the code
In RecyclerView, the Adapter registers the data observer mObserver of RecyclerView. MObserver is held by RecyclerView.
public abstract static class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) { mObservable.unregisterObserver(observer); }}static class AdapterDataObservable extends Observable<AdapterDataObserver> {...//ignore
}
Copy the code
The Observable registerObserver method is actually called:
public abstract class Observable<T> {
protected final ArrayList<T> mObservers = new ArrayList<T>();
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered."); } mObservers.add(observer); }}public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered."); } mObservers.remove(index); }}}Copy the code
As you can see, the Observer is stored in the ArrayList. The Adapter holds a strong reference to the Observer in RecyclerView. When the Fragment switches, the onDestroyView method will be called to release the View. However, the Fragment is not destroyed, and the referenced Adapter will not be released. As a result, RecyclerView will not be released, resulting in memory leakage. The following figure sorts out the citation relationships among the three:
The solution
We found the cause of the problem, which is the periodic reference between objects, so we break the reference relationship between them, as shown in the following figure:
Here we can have the following solutions:
Modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify modify
fun onDestroyView(a) {
mRecyclerView.adapter = null
super.onDestroyView()
}
Copy the code
This usage actually breaks a reference to RecyclerView and Adapter:
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if(mAdapter ! =null) {
// Remove the old Adapter reference to the Observer
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this); }...//ignore
}
Copy the code
This method is suitable for preserving data when switching fragments, so that the original data can be restored when the page is rolled back without creating Adapter again. For example:
private lateinit var mAdapter: BaseAdapter<String>
override fun initView(a) {
// The mAdapter is initialized before it is initialized
if(! ::adapter.isInitialized) { mAdapter = BaseAdapter<String>() } }Copy the code
When onDestroyView of the Fragment emptying the Adapter of the Fragment:
fun onDestroyView(a) {
adapter = null // adapter is nullable
super.onDestroyView()
}
Copy the code
This usage breaks the Fragment and Adapter reference relationship, and the Adapter and RecyclerView can be recycled when the View is destroyed.
It is applicable to recreate Adapter and load data without retaining original data. For example:
private lateinit var adapter: BaseAdapter<ItemData>
override fun initView(a) {
adapter = BaseAdapter<ItemData>()
loadData()
}
private fun loadData(a) {
viewModel.loadData().autoDispose().subscribeBy {}
}
Copy the code
Lifecycle () Lifecycle (); autoCleared (); Lifecycle ();
fun <T : Any> Fragment.autoCleared() = AutoClearedValue<T>(this)
class AutoClearedValue<T : Any> (val fragment: Fragment) : ReadWriteProperty<Fragment.T> {
private var _value: T? = null
init {
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) { fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner -> viewLifecycleOwner? .lifecycle? .addObserver(object : DefaultLifecycleObserver {override fun onDestroy(owner: LifecycleOwner) {
_value = null}})}})}}Copy the code
This method, like the second method, empties the Adapter when the Fragment executes onDestroy, but the extension method automatically retrieves the adapter by listening for the lifecycle and does not require manual handling. Ex. :
private var adapter by autoCleared<BaseAdapter<String>>() // How to use the Fragment
override fun initView(a) {
adapter = BaseAdapter<String>()
mRecyclerView.adapter = adapter
}
Copy the code
reference
A Subtle Memory Leak – Fragment, RecyclerView and its Adapter