D ┬───
D │ GC Root: System class
D │
D ├─ android.os.StrictMode$InstanceTracker class
D │ Leaking: NO (MainActivity↓ is not leaking and a class is never leaking)
D │ ↓ static StrictMode$InstanceTracker.sInstanceCounts
D ├─ java.util.HashMap instance
D │ Leaking: NO (MainActivity↓ is not leaking)
D │ ↓ HashMap[key()]
D ├─ .ui.main.MainActivity class
D │ Leaking: NO (MainActivity↓ is not leaking and a class is never leaking)
D │ ↓ static MainActivity.mMainAct
D ├─ .ui.main.MainActivity instance
D │ Leaking: NO (LifecycleRegistry↓ is not leaking and Activity#mDestroyed is false)
D │ mApplication instance of .app.MyApplication
D │ mEmbeddedApplication instance of .app.MyApplication
D │ mBase instance of androidx.appcompat.view.ContextThemeWrapper
D │ ↓ ComponentActivity.mLifecycleRegistry
D ├─ androidx.lifecycle.LifecycleRegistry instance
D │ Leaking: NO (state is RESUMED)
D │ ↓ LifecycleRegistry.mObserverMap
D │ ~~~~~~~~~~~~
D ├─ androidx.arch.core.internal.FastSafeIterableMap instance
D │ Leaking: UNKNOWN
D │ Retaining 618.4 kB in 10773 objects
D │ ↓ FastSafeIterableMap[key()]
D │ ~~~~~~~
D ├─ androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer$3 instance
D │ Leaking: UNKNOWN
D │ Retaining 617.6 kB in 10742 objects
D │ Anonymous class implementing androidx.lifecycle.LifecycleEventObserver
D │ ↓ FragmentStateAdapter$FragmentMaxLifecycleEnforcer$3.this$1
D │ ~~~~~~
D ├─ androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer instance
D │ Leaking: UNKNOWN
D │ Retaining 617.6 kB in 10741 objects
D │ ↓ FragmentStateAdapter$FragmentMaxLifecycleEnforcer.this$0
D │ ~~~~~~
D ├─ .ui.schedule.adapter.WeekPagerAdapter instance
D │ Leaking: UNKNOWN
D │ Retaining 3.9 kB in 172 objects
D │ ↓ WeekNotePagerAdapter.fragment
D │ ~~~~~~~~
D ╰→ .ui.schedule.fragment.WeekFragment instance
D Leaking: YES (ObjectWatcher was watching this because .ui.schedule.fragment.
D WeekFragment received Fragment#onDestroy() callback. Conflicts with Fragment.mLifecycleRegistry.state
D is INITIALIZED)
根据以上信息,定位到问题代码
D ├─ androidx.arch.core.internal.FastSafeIterableMap instance
D │ Leaking: UNKNOWN
D │ Retaining 618.4 kB in 10773 objects
D │ ↓ FastSafeIterableMap[key()]
D │ ~~~~~~~
D ├─ androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer$3 instance
D │ Leaking: UNKNOWN
D │ Retaining 617.6 kB in 10742 objects
D │ Anonymous class implementing androidx.lifecycle.LifecycleEventObserver
D │ ↓ FragmentStateAdapter$FragmentMaxLifecycleEnforcer$3.this$1
D │ ~~~~~~
D ├─ androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer instance
D │ Leaking: UNKNOWN
D │ Retaining 617.6 kB in 10741 objects
D │ ↓ FragmentStateAdapter$FragmentMaxLifecycleEnforcer.this$0
D │ ~~~~~~
D ├─ .ui.schedule.adapter.WeekPagerAdapter instance
D │ Leaking: UNKNOWN
D │ Retaining 3.9 kB in 172 objects
D │ ↓ WeekNotePagerAdapter.fragment
D │ ~~~~~~~~
D ╰→ .ui.schedule.fragment.WeekFragment instance
那么我们先去看一下我们的 WeekNotePagerAdapter
有什么操作,
class WeekNotePagerAdapter(
fm: FragmentActivity, private var mWeekTimestamps: List<Long>,var fragment:WeekNoteScheduleFragment
) : FragmentStateAdapter(fm) {
val mFragments = SparseArray<ItemWeekNoteFragment>()
private val mPresenter = QuadrantPresenter()
fun setDatas(datas: List<Long>) {
this.mWeekTimestamps = datas
}
override fun containsItem(itemId: Long): Boolean {
return mWeekTimestamps.contains(itemId)
}
override fun getItemId(position: Int): Long {
return mWeekTimestamps[position]
}
override fun getItemCount(): Int {
return mWeekTimestamps.size
}
override fun createFragment(position: Int): Fragment {
val bundle = Bundle()
val weekTimestamp = mWeekTimestamps[position]
bundle.putLong(WEEK_START_TIMESTAMP, weekTimestamp)
val fragment = ItemWeekNoteFragment(fragment)
fragment.arguments = bundle
mFragments.put(position, fragment)
return fragment
}
fun removeFragment(frg: ItemWeekNoteFragment) {
var key = mFragments.indexOfValue(frg)
mFragments.remove(key)
}
}
可以看到,我们调用的adapter
继承自 FragmentStateAdapter
,那么我们去看一下 FragmentStateAdapter
中FragmentMaxLifecycleEnforcer
这个类型对应的参数.
public abstract class FragmentStateAdapter extends
RecyclerView.Adapter<FragmentViewHolder> implements StatefulAdapter {
//....
private FragmentMaxLifecycleEnforcer mFragmentMaxLifecycleEnforcer;
@CallSuper
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
checkArgument(mFragmentMaxLifecycleEnforcer == null);
mFragmentMaxLifecycleEnforcer = new FragmentMaxLifecycleEnforcer();
mFragmentMaxLifecycleEnforcer.register(recyclerView);
}
@CallSuper
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
mFragmentMaxLifecycleEnforcer.unregister(recyclerView);
mFragmentMaxLifecycleEnforcer = null;
}
//.....
}
可以看到 FragmentStateAdapter.mFragmentMaxLifecycleEnforcer
该参数的使用在这两个方法中,我们通过在 WeekNotePagerAdapter
重写 onDetachedFromRecyclerView
方法,并且在该方法内添加 Log
,发现当发生内存泄漏时,该方法并没有被调用。
经过查找资料:发现如果RecyclerView没有正确分离或者释放,那么onDetachedFromRecyclerView就不会被调用。
解决方法一:
因为ViewPager2 中的 mRecyclerView 仅包访问,所以,我们可以通过反射的方式,在该页面的onDestroyView()中
进行手动调用。
val viewPage2 = mBinding?.mViewPager
if (viewPage2 != null) {
val mRecyclerView = viewPage2::class.java.getDeclaredField("mRecyclerView")
mRecyclerView.isAccessible = true
val recyclerView = mRecyclerView.get(viewPage2) as RecyclerView
mWeekNoteAdapter?.onDetachedFromRecyclerView(recyclerView)
}
解决方式二:
override fun onDestroyView() {
super.onDestroyView()
mBinding?.mViewPager?.adapter = null
mWeekNoteAdapter?.mFragments?.clear()
}