Status Update
Comments
tj...@gmail.com <tj...@gmail.com> #2
Or rather the FragmentStateAdapter holds a reference to the ViewPager2 instance.
di...@gmail.com <di...@gmail.com> #3
Here is a workaround When using ViewPager2
inside a Fragment.
Use the FragmentStateAdapter (FragmentManager, Lifecycle)
constructor instead of the FragmentStateAdapter(Fragment)
one and pass the view lifecycle as argument.
FragmentManager fm = getChildFragmentManager();
Lifecycle lifecycle = getViewLifecycleOwner().getLifecycle();
fragmentAdapter = new FragmentAdapter(fm, lifecycle);
tj...@gmail.com <tj...@gmail.com> #4
#3 This works, thank you!
ki...@google.com <ki...@google.com>
ay...@gmail.com <ay...@gmail.com> #5
This was the part which fixed my issue. I was using getLifeCycle() instead.
tj...@gmail.com <tj...@gmail.com> #6
Also seeing a situation where the FragmentLifecycleCallbacks registered here is never unregistered:
private void scheduleViewAttach(final Fragment fragment, @NonNull final FrameLayout container) {
// After a config change, Fragments that were in FragmentManager will be recreated. Since
// ViewHolder container ids are dynamically generated, we opted to manually handle
// attaching Fragment views to containers. For consistency, we use the same mechanism for
// all Fragment views.
mFragmentManager.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
// TODO(
tj...@gmail.com <tj...@gmail.com> #7
Pardon the formatting in the last comment:
private void scheduleViewAttach(final Fragment fragment, @NonNull final FrameLayout container) {
// After a config change, Fragments that were in FragmentManager will be recreated. Since
// ViewHolder container ids are dynamically generated, we opted to manually handle
// attaching Fragment views to containers. For consistency, we use the same mechanism for
// all Fragment views.
mFragmentManager.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
// TODO(b/141956012): Suppressed during upgrade to AGP 3.6.
@SuppressWarnings("ReferenceEquality")
@Override
public void onFragmentViewCreated(@NonNull FragmentManager fm,
@NonNull Fragment f, @NonNull View v,
@Nullable Bundle savedInstanceState) {
if (f == fragment) {
fm.unregisterFragmentLifecycleCallbacks(this);
addViewToContainer(v, container);
}
}
}, false);
}
[Deleted User] <[Deleted User]> #8
de...@gmail.com <de...@gmail.com> #9
su...@gmail.com <su...@gmail.com> #10
da...@gmail.com <da...@gmail.com> #11
bl...@gmail.com <bl...@gmail.com> #12
sa...@gmail.com <sa...@gmail.com> #13
Same problem. It creates new fragments instead of reattaching to the existing one that's displayed.
te...@gmail.com <te...@gmail.com> #14
de...@gmail.com <de...@gmail.com> #15
de...@gmail.com <de...@gmail.com> #16
[Deleted User] <[Deleted User]> #17
I'm still trying to figure out the best solution while waiting for a response from google.
ak...@gmail.com <ak...@gmail.com> #18
jg...@google.com <jg...@google.com> #19
Re
Looking at this issue, it looks like workaround were shared in early comments; while not ideal, still allowing the widget to be used. Ideally we'd be able to fix all issues like this, so workaround wouldn't be needed.
st...@gmail.com <st...@gmail.com> #20
Workaround for this is
OneShotPreDrawListener.add(viewpager2, () -> {viewpager2.setCurrentItem(itemPos);});
only is needed when the fragment ist reattached. but not for initial oncreate which is quite weird.
Hard one to figure out when it works on the initial onCreate
sz...@gmail.com <sz...@gmail.com> #21
I've updated to the latest version 1.1.0-beta02 of ViewPager2, but I still have memory leaks.
pa...@outlook.com <pa...@outlook.com> #22
We stopped using viewpagers in all of our apps as a direct result of this bug, and since there is no priority in fixing it for more than 3 years we also don't plan on using it ever again.
Description
The root culprit is the class FragmentMaxLifecycleEnforcer class. It has the following unregister method
```java
void unregister(@NonNull RecyclerView recyclerView) {
ViewPager2 viewPager = inferViewPager(recyclerView);
viewPager.unregisterOnPageChangeCallback(mPageChangeCallback);
unregisterAdapterDataObserver(mDataObserver);
mLifecycle.removeObserver(mLifecycleObserver);
mViewPager = null;
}
```
The problem is this is only called when the FragmentStateAdapter receives the onDetachedFromRecyclerView callback:
```java
@CallSuper
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
mFragmentMaxLifecycleEnforcer.unregister(recyclerView);
mFragmentMaxLifecycleEnforcer = null;
}
```
This callback is only ever invoked if the adapter changes, and not when the RecyclerView is detached from the window causing the leak as the following two fields are never cleared:
```java
private RecyclerView.AdapterDataObserver mDataObserver;
private LifecycleEventObserver mLifecycleObserver;
```
The only work around at the moment is to manually set the ViewPager2's adapter to null using the KTX View.doOnDetach method.