hegaojian / JetpackMvvm

:chicken::basketball:一个Jetpack结合MVVM的快速开发框架,基于MVVM模式集成谷歌官方推荐的JetPack组件库:LiveData、ViewModel、Lifecycle、Navigation组件 使用Kotlin语言,添加大量拓展函数,简化代码 加入Retrofit网络请求,协程,帮你简化各种操作,让你快速开发项目
https://github.com/hegaojian/JetpackMvvm
Apache License 2.0
3.13k stars 610 forks source link

viewpager2+Fragment 另外一个内存泄漏问题 #62

Closed taichushouwang closed 3 years ago

taichushouwang commented 3 years ago

作者你好,我最近也在使用ViewPager2 + Fragment,发现如果fragment比较多,然后offscreenPageLimit不设置的话,LeakCanary就会报内存泄漏。同样在demo中,如果把你们写的offscreenPageLimit注释掉,然后在多个fragment中来回切换,也会报内存泄漏。这个如果不设置offscreenPageLimit,不知道有没有什么其他好的解决方案

Alexxiaopang commented 3 years ago

内存泄漏是因为没设置offscreenPageLimit,fm回收了,你的fm里面存在持有对象的东西没有销毁,查看内存泄漏的地方,看一下哪里引用没有销毁

taichushouwang commented 3 years ago

内存泄漏是因为没设置offscreenPageLimit,fm回收了,你的fm里面存在持有对象的东西没有销毁,查看内存泄漏的地方,看一下哪里引用没有销毁

这个应该跟FragmentStateAdapter中的list持有FM有关系吧?因为我单独的FM退出了都不会报内存泄漏,但是加到ViewPager里面就会出现。我看网上一些解决方案是不持有list的FM,而是在createFragment里面,去创建FM,但是我的每个FM都不是同一个类型。这种要怎么做

taichushouwang commented 3 years ago

内存泄漏是因为没设置offscreenPageLimit,fm回收了,你的fm里面存在持有对象的东西没有销毁,查看内存泄漏的地方,看一下哪里引用没有销毁

而且我把FM清空,只留一个onCreateView,也一样会报。 @Alexxiaopang

Alexxiaopang commented 3 years ago

内存泄漏是因为没设置offscreenPageLimit,fm回收了,你的fm里面存在持有对象的东西没有销毁,查看内存泄漏的地方,看一下哪里引用没有销毁

而且我把FM清空,只留一个onCreateView,也一样会报。 @Alexxiaopang

我以为你说的是奔溃问题,原来你说的是LeakCanary提示的内存泄漏,这个是viewpager一直都存在的,到了viewpager2也是有的,在你写adapter的时候,不要写成一个list的形式,在需要的地方调用adapter的创建,类似下面,配合viewmodel的SavedStateViewModelFactory保存数据,可以做到数据不变化 val adapter by lazy { object : FragmentStateAdapter(this) { override fun getItemCount(): Int = 12 override fun createFragment(position: Int): Fragment=MatchFragment().getInstant(typeList[position]) } }

taichushouwang commented 3 years ago

内存泄漏是因为没设置offscreenPageLimit,fm回收了,你的fm里面存在持有对象的东西没有销毁,查看内存泄漏的地方,看一下哪里引用没有销毁

而且我把FM清空,只留一个onCreateView,也一样会报。 @Alexxiaopang

我以为你说的是奔溃问题,原来你说的是LeakCanary提示的内存泄漏,这个是viewpager一直都存在的,到了viewpager2也是有的,在你写adapter的时候,不要写成一个list的形式,在需要的地方调用adapter的创建,类似下面,配合viewmodel的SavedStateViewModelFactory保存数据,可以做到数据不变化 val adapter by lazy { object : FragmentStateAdapter(this) { override fun getItemCount(): Int = 12 override fun createFragment(position: Int): Fragment=MatchFragment().getInstant(typeList[position]) } }

那如果我的每一个fragment都不是同一个类呢?分别是FragmentA,FragmentB.... 那这样子是不是得写一个工厂方法来创建不同的Fragment了? @Alexxiaopang

Alexxiaopang commented 3 years ago

内存泄漏是因为没设置offscreenPageLimit,fm回收了,你的fm里面存在持有对象的东西没有销毁,查看内存泄漏的地方,看一下哪里引用没有销毁

而且我把FM清空,只留一个onCreateView,也一样会报。 @Alexxiaopang

我以为你说的是奔溃问题,原来你说的是LeakCanary提示的内存泄漏,这个是viewpager一直都存在的,到了viewpager2也是有的,在你写adapter的时候,不要写成一个list的形式,在需要的地方调用adapter的创建,类似下面,配合viewmodel的SavedStateViewModelFactory保存数据,可以做到数据不变化 val adapter by lazy { object : FragmentStateAdapter(this) { override fun getItemCount(): Int = 12 override fun createFragment(position: Int): Fragment=MatchFragment().getInstant(typeList[position]) } }

那如果我的每一个fragment都不是同一个类呢?分别是FragmentA,FragmentB.... 那这样子是不是得写一个工厂方法来创建不同的Fragment了? @Alexxiaopang

可以用工厂也可以直接在需要的地方做when判断,方法多种多样!list存储是一直以来都不建议的,因为无论是1还是2在没设置offlint的情况下都会把不是相邻的fm销毁,你用list强引用本来就是不符合规定的,但是因为不会有啥大问题,所以大家都一直这样做!

taichushouwang commented 3 years ago

可以用工厂也可以直接在需要的地方做when判断,方法多种多样!list存储是一直以来都不建议的,因为无论是1还是2在没设置offlint的情况下都会把不是相邻的fm销毁,你用list强引用本来就是不符合规定的,但是因为不会有啥大问题,所以大家都一直这样做!

@Alexxiaopang 非常感谢你的耐心解答!我对ViewPager不太熟悉,然后网上也都是用的同一个Fragment,你说的”在需要的地方做when判断“,这个我不是太了解,是否可以说一下具体的代码呢?如果是按照我的方案,我的代码如下,但是这样的话,就得每个ViewPager写一个Adapter,又或者用工厂模式,那也是每加一个Fragment,就要添加一个创建方法,总觉得没有以前的方便了。

class TabTextLayoutAdapter2(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
        FragmentStateAdapter(fragmentManager, lifecycle) {
    var fragmentTitles: MutableList<String> = ArrayList()

    fun addItem(title: String) {
        fragmentTitles.add(title)
    }

    override fun getItemCount(): Int {
        return fragmentTitles.size
    }

    override fun createFragment(position: Int): Fragment {
        return when (fragmentTitles[position]) {
            "FragmentA" -> FragmentA()
            "FragmentB" -> FragmentB()
            "FragmentC" -> FragmentC()
            "FragmentD" -> FragmentD()
            else -> Fragment()
        }
    }
}
Alexxiaopang commented 3 years ago

可以用工厂也可以直接在需要的地方做when判断,方法多种多样!list存储是一直以来都不建议的,因为无论是1还是2在没设置offlint的情况下都会把不是相邻的fm销毁,你用list强引用本来就是不符合规定的,但是因为不会有啥大问题,所以大家都一直这样做!

@Alexxiaopang 非常感谢你的耐心解答!我对ViewPager不太熟悉,然后网上也都是用的同一个Fragment,你说的”在需要的地方做when判断“,这个我不是太了解,是否可以说一下具体的代码呢?如果是按照我的方案,我的代码如下,但是这样的话,就得每个ViewPager写一个Adapter,又或者用工厂模式,那也是每加一个Fragment,就要添加一个创建方法,总觉得没有以前的方便了。

class TabTextLayoutAdapter2(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
        FragmentStateAdapter(fragmentManager, lifecycle) {
    var fragmentTitles: MutableList<String> = ArrayList()

    fun addItem(title: String) {
        fragmentTitles.add(title)
    }

    override fun getItemCount(): Int {
        return fragmentTitles.size
    }

    override fun createFragment(position: Int): Fragment {
        return when (fragmentTitles[position]) {
            "FragmentA" -> FragmentA()
            "FragmentB" -> FragmentB()
            "FragmentC" -> FragmentC()
            "FragmentD" -> FragmentD()
            else -> Fragment()
        }
    }
}

直接通过position就可以了,还有就是,你进入了误区,这个是内存溢出不是内存泄漏,你需要处理很多fm的情况在一个app内并不多见,不会说出现每个都需要重写adapter,对于那些不可见的内页还有不是很多fm的Tab,建议使用offlint+list,这也就是为啥viewpager2会基于recyclerview。更多的场景google更希望你用recyclerview.adapter。分场景去使用代码!

taichushouwang commented 3 years ago

”你进入了误区,这个是内存溢出不是内存泄漏“,

这个我不是太明白。按照你之前说的:

list存储是一直以来都不建议的,因为无论是1还是2在没设置offlint的情况下都会把不是相邻的fm销毁,你用list强引用本来就是不符合规定的

这里使用了强引用,然后需要销毁又没有被销毁,那不就是内存泄露了吗?

18810951092 commented 3 years ago

sycn Download fragment-1.2.4.aar 是什么鬼 今天同步项目怎么加载不了了

haoYeaph commented 11 months ago

那怎么解决这个内存溢出问题?即使这样写还是小鸟会报内存问题