Fixed
Status Update
Comments
mo...@google.com <mo...@google.com>
mo...@google.com <mo...@google.com> #2
Found the issue, ugh
Noticed that when I do the following:
transaction.addToBackStack(null)
When adding a Fragment with ViewPager2, the above issue occurs, although, idk if it's becaugh of gcFragments() anymore.
As soon as I un-comment that line, the issue disappears.
Noticed that when I do the following:
transaction.addToBackStack(null)
When adding a Fragment with ViewPager2, the above issue occurs, although, idk if it's becaugh of gcFragments() anymore.
As soon as I un-comment that line, the issue disappears.
va...@google.com <va...@google.com> #3
Is the Sample application, to reproduce, move back really fast between page 2 -> 1 and vice versa (they are bottom buttons) a couple times.
Wait a couple seconds and page 1 (the black page) should turn white meaning the Fragment has been removed.
As soon as you comment line 40 on MainActivity, and attempt again, the error does not persist
va...@google.com <va...@google.com> #4
Woops, sorry, If you guys had the previous ViewPagerCrash test app, I forgot to push (was exhasuted after debugging) I pushed one more time for the latest reproducible code
jb...@google.com <jb...@google.com> #5
We'll have a look, thanks for reporting and repro steps / sample app. Stay tuned for updates on this issue.
jb...@google.com <jb...@google.com> #6
Got to debug this now. Your sample indeed showcases an issue, so thanks for reporting and for the sample!
`transaction.addToBackStack(null)` adds the Fragment replacement transaction to the back stack, so you can go back to the previous state with the back button. Is that what you were looking to achieve? You said it works without it, which might help in the meantime.
Would you mind describing the use-case, so I better understand what you're trying to achieve? E.g. in the example, by switching between buttons 1 and 2 at some point you achieve multiple stack entries going back to the ViewPager2 Fragment.
`transaction.addToBackStack(null)` adds the Fragment replacement transaction to the back stack, so you can go back to the previous state with the back button. Is that what you were looking to achieve? You said it works without it, which might help in the meantime.
Would you mind describing the use-case, so I better understand what you're trying to achieve? E.g. in the example, by switching between buttons 1 and 2 at some point you achieve multiple stack entries going back to the ViewPager2 Fragment.
ap...@google.com <ap...@google.com> #7
Mostly for my notes, but the trace below explains what happens. Still need to work out why.
We have 5 instances of FragmentStateAdapter below.
Notice how in lines 36-39 4 different instances of FragmentStateAdapter perform a scheduled gc (after grace period expires).
Those don't have the Fragments bound to their ViewHolders as those were removed: lines 8, 16, 24, 32.
At the same time, com.test.myapplication.VPAdapter.createFragment(ViewPagerFragment.kt:161) only gets called once, so presumably all 5 have a reference to that same Fragment.
Needs more investigation as looks fishy.
1 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11289)'
2 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
3 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
4 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
5 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
6 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
7 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
8 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
9 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11307)'
10 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
11 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
12 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
13 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
14 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
15 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
16 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
17 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11314)'
18 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
19 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
20 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
21 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
22 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
23 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
24 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
25 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11316)'
26 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
27 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
28 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
29 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
30 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
31 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
32 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
33 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11318)'
34 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
35 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
36 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
37 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
38 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
39 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
We have 5 instances of FragmentStateAdapter below.
Notice how in lines 36-39 4 different instances of FragmentStateAdapter perform a scheduled gc (after grace period expires).
Those don't have the Fragments bound to their ViewHolders as those were removed: lines 8, 16, 24, 32.
At the same time, com.test.myapplication.VPAdapter.createFragment(ViewPagerFragment.kt:161) only gets called once, so presumably all 5 have a reference to that same Fragment.
Needs more investigation as looks fishy.
1 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11289)'
2 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
3 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
4 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
5 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
6 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
7 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
8 {FragmentStateAdapter@11,288}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
9 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11307)'
10 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
11 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
12 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
13 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
14 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
15 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
16 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
17 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11314)'
18 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
19 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
20 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
21 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
22 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
23 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
24 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
25 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11316)'
26 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
27 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
28 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
29 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:235)
30 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:240)
31 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
32 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onViewRecycled(FragmentStateAdapter.java:391)
33 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be modified at FragmentStateAdapter.<init>(FragmentStateAdapter.java:85). Current value = 'null'. New value = 'instance of androidx.collection.LongSparseArray(id=11318)'
34 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.itemForViewHolder(FragmentStateAdapter.java:234)
35 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.onBindViewHolder(FragmentStateAdapter.java:174)
36 {FragmentStateAdapter@11,306}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
37 {FragmentStateAdapter@11,313}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
38 {FragmentStateAdapter@11,315}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
39 {FragmentStateAdapter@11,317}.mItemIdToViewHolder will be accessed at FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:221)
mo...@google.com <mo...@google.com>
mo...@google.com <mo...@google.com> #9
Yes, completely correct that I am attempting to use the back pressed implementation but also a bottom navigation implementation to switch to and from Fragments.
Currently the app that I am trying to make contains multiple Fragments with and without ViewPager2's accessible through a bottom navigation set of buttons. Through simple usage of moving to and from my ViewPager2 Fragments, I would see random instances of child Fragments disappearing from them.
Currently the app that I am trying to make contains multiple Fragments with and without ViewPager2's accessible through a bottom navigation set of buttons. Through simple usage of moving to and from my ViewPager2 Fragments, I would see random instances of child Fragments disappearing from them.
mo...@google.com <mo...@google.com> #10
I'd rather like to keep the "addToBackStack(..)" to rewind through fragment transactions. Will definitely look further into it also from the above mentioned logs.
ap...@google.com <ap...@google.com> #11
Thanks for clarifying, I will investigate further.
Following on the logs above, I suspect that each instance of FragmentStateAdapter gets access to that one BaseFragment in FragmentStateAdapter::restoreState (restore state called on each stack entry).
Making sure each ViewPagerFragment instance only gets access to its own state (so the state is not shared between them) should eliminate this issue.
Not sure where the problem lies yet, whether in ViewPager2's interaction with the state saving stack, or in the sample app, so continuing to investigate.
Following on the logs above, I suspect that each instance of FragmentStateAdapter gets access to that one BaseFragment in FragmentStateAdapter::restoreState (restore state called on each stack entry).
Making sure each ViewPagerFragment instance only gets access to its own state (so the state is not shared between them) should eliminate this issue.
Not sure where the problem lies yet, whether in ViewPager2's interaction with the state saving stack, or in the sample app, so continuing to investigate.
pr...@google.com <pr...@google.com> #12
Yeah.. I have no clue what's going on, spending too much time debugging. I am planning to attach LifecycleObserver to my ViewHolder's so they act like Fragments.
From the evidence above, I am pretty sure it should not affect ViewHolder pattern but never know. Would love to know what was happening though if you come across a solution.
From the evidence above, I am pretty sure it should not affect ViewHolder pattern but never know. Would love to know what was happening though if you come across a solution.
Description
Component used: Transition Version used: 1.5.0-alpha02 Devices/Android versions reproduced on: API 34
If you do a gesture back in Fragments using transitions and cancel the gesture multiple times, after the first cancel, starting the gesture will result in the transition on the exiting view failing to run.
I believe the cause of this is that the this
beginDelayedTransition()
called by with a 0 duration called by Fragment never triggersonAnimatedEnd()
callback inVisibility
. That results in the visibility failing to be added removed from the overlay and so the subsequent transitions on the view do not run.Here are logs synced with the calls from Fragment and Visibility. I would expect there to be an
onAnimationEnd reversed=false
log before theonTransitionEnd
in Visibility.The can be reproduced by patching in aosp/2748867 and doing the following in the navigation-integration app :