项目内存泄漏实录——匿名内部类

发现问题

前段时间在用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匿名内部类来直接给这个Viewpageradapter赋值,同时我从外部接收了一个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要被销毁时,将ViewPageradapter置为null

class MainFragment : BaseFragment<HomeFragmentViewModel, FragmentMainBinding>() {
	...
	override fun onDestroyView() {
		mDatabind.mainVp.adapter = null
		super.onDestroyView()
	}
	...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值