Search code examples
androidandroid-fragmentsandroid-viewpagerandroid-dialogfragmentandroid-viewpager2

ViewPager2 not destroying fragments


I have a DialogFragment with a ViewPager2:

public class LightboxFragment extends DialogFragment {

    private ViewPager2 viewPager;

    private MyViewPagerAdapter myViewPagerAdapter;

    public static LightboxFragment newInstance() {
        LightboxFragment fragment = new LightboxFragment();

        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_lightbox, container, false);

        viewPager = v.findViewById(R.id.viewPager);

        FrameLayout closeButtonContainer = v.findViewById(R.id.close_button_container);
        closeButtonContainer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

        myViewPagerAdapter = new MyViewPagerAdapter(getActivity());
        viewPager.setAdapter(myViewPagerAdapter);

        return v;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    public class MyViewPagerAdapter extends FragmentStateAdapter {

        public MyViewPagerAdapter(FragmentActivity fa) {
            super(fa);
        }

        @NonNull
        @Override
        public Fragment createFragment(int position) {
            LightboxVideoFragment fragment = new LightboxVideoFragment();
            return fragment;
        }

        @Override
        public int getItemCount() {
            return items.size();
        }

    }

}

And my LightboxVideoFragment looks something like this:

public class LightboxVideoFragment extends Fragment {

    private SimpleExoPlayer exoPlayer;

    private PlayerView playerView;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.slideshow_video, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        playerView = view.findViewById(R.id.player_view);

        MediaItem mediaItem = MediaItem.fromUri(media.getUrls().getMp4());
        exoPlayer = new SimpleExoPlayer.Builder(getContext()).build();
        exoPlayer.setRepeatMode(Player.REPEAT_MODE_ALL);
        playerView.setPlayer(exoPlayer);
        exoPlayer.setMediaItem(mediaItem);
        exoPlayer.prepare();
        exoPlayer.play();
    }

    @Override
    public void onResume() {
        super.onResume();

        if (exoPlayer != null) {
            exoPlayer.play();
        }
    }

    @Override
    public void onPause() {
        super.onPause();

        if (exoPlayer != null) {
            exoPlayer.pause();
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();

        if (exoPlayer != null) {
            exoPlayer.release();
        }
    }

}

My problem is this: When the DialogFragment is dismissed, the audio from the fragment's video is still playing. Why is this? I've tried adding onDestroy in my fragment and setting breakpoints there, but the breakpoint is never called when the dialog is dismissed, nor is onPause or onDetach.

Does this also mean that the ExoPlayer instance is also still alive when the dialog is dismissed?

How can I stop the video's audio when the dialog is dismissed?


Solution

  • I don't use viewpager2 inside another Fragment but based on the documentation and the source code of viewpager2 I think you are creating the adapter incorrectly when doing it from a Fragment.

    In the class LightboxFragment try changing line

    myViewPagerAdapter = new MyViewPagerAdapter(getActivity());

    to

    myViewPagerAdapter = new MyViewPagerAdapter(this);

    This is because based on the documentation https://developer.android.com/reference/androidx/viewpager2/adapter/FragmentStateAdapter#FragmentStateAdapter(androidx.fragment.app.Fragment)

    Fragment: if the ViewPager2 lives directly in a Fragment subclass.

    You are using it as if the Viewpager2 is living directly in the host activity.

    This means you are putting LightboxVideoFragment in the same FragmentManager as LightboxFragment and the Lifecycle changes to LightboxFragment don't affect what are basically siblings to it (not children as they should be)

    If you look at the source code of viewpager2 for the constructors as well

    https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java#102

    The correct constructor ties the Fragments in the viewpager2 adaptor to a host Fragment via getChildFragmentManager() and the Fragment's lifecycle and not to the Activity's lifecycle.

    (I've not experimented or tested this just knowledge from the docs and reading the source code).