关键点
FragmentStateAdapter是继承自RecyclerView.Adapter,ViewPage2是基于RecyclerView进行实现的。FragmentStateAdapter在实例化的时候,会持有一个Lifecycle对象,而该Lifecycle对象从Fragment或者Activity得来。
源码解析
FragmentStateAdapter{
...
//该方法是RecyclerView.Adapter的回调,在RecyclerView调用setAdapter时进入
@CallSuper
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
checkArgument(mFragmentMaxLifecycleEnforcer == null);
mFragmentMaxLifecycleEnforcer = new FragmentMaxLifecycleEnforcer();
mFragmentMaxLifecycleEnforcer.register(recyclerView);
}
//该方法是RecyclerView.Adapter的回调,在RecyclerView首次调用setAdapter时不进入,之后每次调用setAdapter都进入
@CallSuper
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
mFragmentMaxLifecycleEnforcer.unregister(recyclerView);
mFragmentMaxLifecycleEnforcer = null;
}
//内部类
class FragmentMaxLifecycleEnforcer {
private ViewPager2.OnPageChangeCallback mPageChangeCallback;
private RecyclerView.AdapterDataObserver mDataObserver;
private LifecycleEventObserver mLifecycleObserver;
private ViewPager2 mViewPager;
private long mPrimaryItemId = NO_ID;
void register(@NonNull RecyclerView recyclerView) {
//利用传入的recyclerView,取recyclerView.getParent()来获得ViewPage2
mViewPager = inferViewPager(recyclerView);
mPageChangeCallback = new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrollStateChanged(int state) {
updateFragmentMaxLifecycle(false);
}
@Override
public void onPageSelected(int position) {
updateFragmentMaxLifecycle(false);
}
};
mViewPager.registerOnPageChangeCallback(mPageChangeCallback);
mDataObserver = new DataSetChangeObserver() {
@Override
public void onChanged() {
updateFragmentMaxLifecycle(true);
}
};
registerAdapterDataObserver(mDataObserver);
mLifecycleObserver = new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//接口的实现,造成内存泄漏的关键点,这里持有外部对象mViewPager的引用
updateFragmentMaxLifecycle(false);
}
};
//造成内存泄漏的关键点,这里把属于Fragment的Lifecycle对象,注册了一个LifecycleEventObserver对象
mLifecycle.addObserver(mLifecycleObserver);
}
void unregister(@NonNull RecyclerView recyclerView) {
ViewPager2 viewPager = inferViewPager(recyclerView);
viewPager.unregisterOnPageChangeCallback(mPageChangeCallback);
unregisterAdapterDataObserver(mDataObserver);
//释放掉内存泄漏的关键点,因为该方法只有第二次setAdapter才会进入,所以在销毁的时候需要调用setAdapter(null)
mLifecycle.removeObserver(mLifecycleObserver);
mViewPager = null;
}
}
}
RecyclerView{
public void setAdapter(@Nullable Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
//这里调用了下面的setAdapterInternal方法
setAdapterInternal(adapter, false, true);
processDataSetCompletelyChanged(false);
requestLayout();
}
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
//第一次进入时mAdapter为null
if (mAdapter != null) {
//第二次之后mAdapter不为null
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
removeAndRecycleViews();
}
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
}
}
场景分析
- 场景
因为Navigation的设计机制,Fragment是利用replace跳转的,在跳转页面时会调用onDestroyView(),而该方法调用后,在onCreateView()函数里所创建的View需要销毁。
- 造成内存泄漏的原因
因为FragmentStateAdapter会持有Fragment的Lifecycle对象,并且在recyclerView.setAdapter时会注册LifecycleEventObserver,而LifecycleEventObserver对象的实现持有了外部类的ViewPager2引用,在Lifecycle对象没释放掉LifecycleEventObserver对象时,就代表着Fragment的Lifecycle引用了ViewPager2对象,从而导致onDestroyView()函数执行时,没办法释放掉onCreateView()所创建的对象。
- 解决方案
onDestroyView()里面调用recyclerView.setAdapter(null)。