发现问题
前段时间在用LeakCanary检测项目内存泄漏的时候发现主页总是出现泄漏提示,根据日志发现问题出现在下面这个Fragment中
class MainFragment : BaseFragment<HomeFragmentViewModel, FragmentMainBinding>() {
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mDatabind.mainVp.initMain(this) //关键是这一行
...
}
...
}
mDatabind.mainVp
是是一个ViewPager
对象,initMain()
是ViewPager
的一个扩展函数,用于给这个ViewPager
设置适配器,具体代码如下
fun ViewPager2.initMain(fragment: Fragment): ViewPager2 {
...
adapter = object : FragmentStateAdapter(fragment) {
override fun createFragment(position: Int): Fragment {
when (position) {
0 -> {
return HomeFragment()
}
1 -> {
return NotificationFragment()
}
2 -> {
return MeFragment()
}
else -> {
return HomeFragment()
}
}
}
override fun getItemCount() = 3
}
return this
}
分析
首先所谓内存泄漏是什么呢,其实就是长生命周期对象持有短生命周期对象的引用,导致短生命周期对象在该被销毁的时候却因为长生命周期对象对其的引用导致其无法被销毁,这就造成了内存泄漏。
那么回过头来看上面的代码,在initMain()
方法中,我实现了一个FragmentStateAdapter
匿名内部类来直接给这个Viewpager
的adapter
赋值,同时我从外部接收了一个fragment对象,这个fragment对象也就是外部类,它作为参数被传入给FragmentStateAdapter
匿名内部类进行管理,而这就是问题所在:这个FragmentStateAdapter匿名内部类会持有fragment外部类的引用。当外部fragment要被销毁时,而因为这个匿名内部类还没有给销毁且一直持有这个外部fragment,导致这个fragment无法被销毁,这就造成了内存泄漏。
解决方法
知道了原理后要解决就很简单了
方案一
用一个顶层类实现FragmentStateAdapter的功能,而非直接用匿名内部类
class MainFragmentStateAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> HomeFragment()
1 -> NotificationFragment()
2 -> MeFragment()
else -> HomeFragment()
}
}
override fun getItemCount() = 3
}
fun ViewPager2.initMain(fragment: Fragment): ViewPager2 {
...
this.adapter = MainFragmentStateAdapter(fragment)
return this
}
方案二
在fragment要被销毁时,将ViewPager
的adapter
置为null
class MainFragment : BaseFragment<HomeFragmentViewModel, FragmentMainBinding>() {
...
override fun onDestroyView() {
mDatabind.mainVp.adapter = null
super.onDestroyView()
}
...
}