views-widgets-samples icon indicating copy to clipboard operation
views-widgets-samples copied to clipboard

Viewpager2 FragmentStateAdapter leak

Open PierreFourreau opened this issue 5 years ago • 19 comments

Hi,

I implement a basic viewpager2

My main fragment (which contain a viewpager of 2 fragments (fragmentA and fragment B))

...
 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 my_view_pager.adapter = MyAdapter(activity, args.refBrand)
        TabLayoutMediator(my_tab_layout, view_pager) { tab, position ->
            tab.text = if(position == 0) "1" else "2"
        }.attach()
}
...

Adapter :

class MyAdapter : FragmentStateAdapter(fragmentActivity) {

    override fun getItemCount(): Int = 2

    override fun createFragment(position: Int): Fragment = when (position) {
        0 -> FirstFragment.newInstance(filter = true)
        1 -> Second.newInstance(filter = false)
        else -> throw IllegalArgumentException...
    }
}

The FragmentStateAdapter is leaking.

Like my others screens without viewpagers I tried

override fun onDestroyView() {
        super.onDestroyView()
        my_view_pager.adapter = null
    }

But it crash when I go to another fragment destination from fragmentA (or B) and come back :

E/AndroidRuntime: FATAL EXCEPTION: main Process: app, PID: 30471 java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id 7ee10572-a2f6-4e46-8227-daaad57fd649 at androidx.fragment.app.FragmentManager.getFragment(FragmentManager.java:772) at androidx.viewpager2.adapter.FragmentStateAdapter.restoreState(FragmentStateAdapter.java:549) at androidx.viewpager2.widget.ViewPager2.restorePendingState(ViewPager2.java:350) at androidx.viewpager2.widget.ViewPager2.dispatchRestoreInstanceState(ViewPager2.java:375) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864) at android.view.View.restoreHierarchyState(View.java:19793) at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:573) at androidx.fragment.app.FragmentStateManager.restoreViewState(FragmentStateManager.java:356) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1189) at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849) at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Can you help? Thanks

PierreFourreau avatar Mar 27 '20 22:03 PierreFourreau

Same problem here

hushino avatar May 14 '20 12:05 hushino

When using a Navigation Graph which loads a contain fragment which contains FragmentStateAdapter

Its also reported here: https://issuetracker.google.com/issues/154751401

jermgilb avatar Jun 03 '20 19:06 jermgilb

I had the same problem but it looks like I have found a workaround. In my case, I was adding data to the adapter before attaching it to the ViewPager2 adapter. If I first set the ViewPager2 adapter and then add data then looks like the FragmentStateAdapter is no longer leaking and the app is not crashing

AleksandarAleksiev avatar Oct 16 '20 09:10 AleksandarAleksiev

The same problem. My solution: viewPager2.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { override fun onViewDetachedFromWindow(v: View) { viewPager2.removeOnAttachStateChangeListener(this) tabLayoutMediator.detach() viewPager2.setup.unregisterOnPageChangeCallback(onPageChangeCallback) viewPager2..adapter = null } })

FragmentStateAdapter is still leaking because it uses lifecycle from mainnavigationfragment. But it uses only 800bytes.

akelix avatar Dec 28 '20 09:12 akelix

Using viewpager.adapter = null in onDestroyView() is causing this issue, removing this line avoid the crash. But nullify the ViewPager's adapter is an attempt to fix the memory leak which should be fixed with a yet unknown efficient manner 😞

Rajarml avatar Aug 10 '21 10:08 Rajarml

You can try to set this layout property for androidx.viewpager2.widget.ViewPager2 android:saveEnabled="false"

vlasentiy avatar Nov 04 '21 14:11 vlasentiy

Just create adapter with Fragment: FragmentStateAdapter(fragment)

Don't use FragmentStateAdapter(fragmentActivity)

alkocher avatar Dec 06 '21 18:12 alkocher

You can try to set this layout property for androidx.viewpager2.widget.ViewPager2 android:saveEnabled="false"

U are my lifesaver! my viewpager was already inside a fragment, and I try the childFragmentManager but the app will crash when I press again, but adding this"savedEnabled=false", it works perfectly!

OonKhengHuang avatar Apr 06 '22 13:04 OonKhengHuang

According to https://issuetracker.google.com/issues/151212195#comment3

FragmentStateAdapter(fragment.childFragmentManager, fragment.viewLifecycleOwner.lifecycle)

hoc081098 avatar May 31 '22 19:05 hoc081098

Has someone found a fix for this issue yet? I have tried quite a few things and only android:saveEnabled="false seems to work, but I need the ViewPager2 to remember the page it is on

VinuPolly-Bonnet avatar Sep 14 '22 21:09 VinuPolly-Bonnet

Has someone found a fix for this issue yet? I have tried quite a few things and only android:saveEnabled="false seems to work, but I need the ViewPager2 to remember the page it is on

Try to use FragmentStateAdapter(fragment, lifecycle)

alkocher avatar Sep 14 '22 21:09 alkocher

@alkocher thanks, but isn't it same as using FragmentStateAdapter(fragment), because I will be anyways using the host fragments childFragmentManager and hostFragment's lifeCycle. Maybe I am missing something here?

VinuPolly-Bonnet avatar Sep 14 '22 21:09 VinuPolly-Bonnet

@alkocher thanks, but isn't it same as using FragmentStateAdapter(fragment), because I will be anyways using the host fragments childFragmentManager and hostFragment's lifeCycle. Maybe I am missing something here?

I use this constructor with fragment.viewLifecycleOwner.lifecycle and set adapter to null when onDestroyView.

alkocher avatar Sep 14 '22 21:09 alkocher

@alkocher thanks, will try this out 🙌

VinuPolly-Bonnet avatar Sep 14 '22 23:09 VinuPolly-Bonnet

You can try to set this layout property for androidx.viewpager2.widget.ViewPager2 android:saveEnabled="false"

brooooo you saved my time and nerves today!

DiamondIT7 avatar Feb 21 '23 13:02 DiamondIT7

You can try to set this layout property for androidx.viewpager2.widget.ViewPager2 android:saveEnabled="false"

Thank you for this . Finally, I can fix it.

MINTHANHTIKE25 avatar Jun 16 '23 18:06 MINTHANHTIKE25

Just create adapter with Fragment: FragmentStateAdapter(fragment)

Don't use FragmentStateAdapter(fragmentActivity)

It didn't help in my case(

AlyonaRulezzz avatar Nov 12 '23 16:11 AlyonaRulezzz

You can try to set this layout property for androidx.viewpager2.widget.ViewPager2 android:saveEnabled="false"

Thanks!

HeXioahei avatar May 23 '24 14:05 HeXioahei