前言
在项目开发中发现在 Fragment 使用 RecyclerView 时导致内存泄漏问题,记录一下问题原因以及解决办法。
原因分析
通常 Fragment 中使用 RecyclerView 是直接在 onCreateView 方法中初始化并设置 Adapter , 一般都会这样写:
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
}
我们来看看 RecyclerView 的代码:
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) {
// 注册 RecyclerView 的数据观察
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
}
在 RecyclerView 中 adapter 注册了 RecyclerView 的数据观察者 mObserver,mObserver 被 RecyclerView 持有,接着来看下 Adapter :
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
}
实际调用到的是 Observable 的 registerObserver 方法:
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);
}
}
}
可以看到观察者 Observer 被保存到了 ArrayList 中, Adapter 持有 RecyclerView 中 Observer 的强引用。当 Fragment 切换时,虽然会调用 onDestroyView 方法进行 View 的释放工作,但是 Fragment 未销毁,引用的 Adapter 不会释放,导致 RecyclerView 也不被释放,造成内存泄漏。下图梳理了三者之间的引用关系:
解决办法
即然找到了问题的原因,是对象间存在周期引用,那我们就打断它们之间的引用关系,如下图:
这里我们可以有以下几种解决思路:
1. Fragment 的 onDestroyView 时把 RecyclerView 的 Adapter 置空 :
fun onDestroyView() {
mRecyclerView.adapter = null
super.onDestroyView()
}
此用法实际是断开 RecyclerView 和 Adapter 的引用:
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
// 注销 RecyclerView 的数据观察 , 移除旧 Adapter 对 Observer 的引用
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
... //ignore
}
这种方法适用于切换 Fragment 时需要保留数据,方便页面回退时恢复原来的数据,不需要重新创建 Adapter ,例:
private lateinit var mAdapter: BaseAdapter<String>
override fun initView() {
// mAdapter 未初始化时才进行初始化
if (!::adapter.isInitialized) {
mAdapter = BaseAdapter<String>()
}
}
2. Fragment 的 onDestroyView 时把 Fragment 的 Adapter 置空:
fun onDestroyView() {
adapter = null // adapter is nullable
super.onDestroyView()
}
这种使用方式断开了 Fragment 和 Adapter 的引用关系,View 销毁时 Adapter 和 RecyclerView 可以被回收。
适用于不需要保留原始数据,界面恢复重新创建 Adapter 并加载数据的方式,例:
private lateinit var adapter: BaseAdapter<ItemData>
override fun initView() {
adapter = BaseAdapter<ItemData>()
loadData()
}
private fun loadData() {
viewModel.loadData().autoDispose().subscribeBy {}
}
3. Lifecycle 监听生命周期,复写 Fragment 扩展方法 autoCleared 自动实现 adapter 赋空操作:
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
}
})
}
}
})
}
}
这种方法和第二种方法一样,都是在 Fragment 执行 onDestroy 方法时对 adapter 赋空,但是扩展方法通过对生命周期的监听实现了自动回收操作,不需要我们手动处理。例:
private var adapter by autoCleared<BaseAdapter<String>>() // Fragment 中的使用方式
override fun initView() {
adapter = BaseAdapter<String>()
mRecyclerView.adapter = adapter
}
参考文献
A Subtle Memory Leak - Fragment, RecyclerView and its Adapter