Как закрыть экземпляр ExoPlayer в ViewPager2, когда происходит свайп - PullRequest
0 голосов
/ 14 марта 2020

У меня есть следующий фрагмент, обрабатывающий ViewPager2, который создает фрагменты (VideoFragment), на которых видео показывается через ExoPlayer:

private const val IMMERSIVE_FLAG_TIMEOUT = 500L
class VideoGalleryFragment : Fragment() {

    private lateinit var binding: FragmentVideoGalleryBinding
    private lateinit var mediaList: MutableList<File>
    private lateinit var mediaViewPager: ViewPager2

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory

    private val videoGalleryFragmentViewModel by viewModels<VideoGalleryFragmentViewModel> { viewModelFactory }

    private val args by navArgs<VideoGalleryFragmentArgs>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Mark this as a retain fragment, so the lifecycle does not get restarted on config change
        retainInstance = true

        // Get root directory of media
        //outputDirectory = getOutputDirectory(requireContext())
        val rootDirectory = File(args.rootDirectory)


        // Walk through all files in the root directory
        // We reverse the order of the list to present the last photos first
        mediaList = rootDirectory.listFiles { file ->
            VIDEO_EXTENSION_WHITELIST.contains(file.extension.toUpperCase(Locale.ROOT))
        }?.sortedDescending()?.toMutableList() ?: mutableListOf()

    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        // Inflate the layout for this fragment
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_video_gallery, container, false)
        binding.apply {
            lifecycleOwner = viewLifecycleOwner
            viewModel = videoGalleryFragmentViewModel
        }

        return binding.root
    }

    // HERE IS MY VIEWPAGER2 SETUP
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        mediaViewPager = binding.videoViewPager.apply {
            offscreenPageLimit = 2
            adapter = object: FragmentStateAdapter(this@VideoGalleryFragment) {
                override fun getItemCount(): Int = mediaList.size

                override fun createFragment(position: Int): Fragment =
                    VideoFragment.create(
                        mediaList[position]
                    )

            }
            setPageTransformer(DepthPageTransformer())
        }


        }
    }



    override fun onResume() {
        super.onResume()
        /*
        * Before setting full screen flags, we must wait a bit to let UI settle; otherwise, we may
        * be trying to set app to immersive mode before it's ready and the flags do not stick
        * */
        binding.container.postDelayed({
            binding.container.systemUiVisibility =
                FLAGS_FULLSCREEN
        }, IMMERSIVE_FLAG_TIMEOUT)
    }
}

А вот класс Fragment (используется ViewPager2), показывающий видео через ExoPlayer2:

class VideoFragment : Fragment() {

    private var _binding: FragmentVideoBinding? = null
    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    private var player: SimpleExoPlayer? = null

    private var playWhenReady: Boolean? = true
    private var currentWindow: Int? = 0
    private var playbackPosition : Long? = 0
    private var resource: String? = null


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        _binding = FragmentVideoBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val args = arguments ?: return
        args.getString(VideoFragment.VIDEO_FILE_NAME_KEY)?.let {
            resource = it
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        releasePlayer()
        _binding = null
    }

    override fun onStart() {
        super.onStart()
        if(Util.SDK_INT >= 24){
            initializePlayer(resource)
        }
    }

    override fun onResume() {
        super.onResume()
        if((Util.SDK_INT < 24 || player == null)){
            initializePlayer(resource)
        }
    }

    private fun initializePlayer(videoFilePath: String?){

        player = ExoPlayerFactory.newSimpleInstance(activity)




        binding.videoViewGallery.player = player


        videoFilePath?.let {
            val mediaSource: MediaSource =
                ProgressiveMediaSource.Factory(DefaultDataSourceFactory(activity, "exoplayer-mylim"))
                    .createMediaSource(Uri.parse(it))


            playWhenReady?.let { playWhenReady ->
                (player as SimpleExoPlayer).playWhenReady = playWhenReady
            }



            currentWindow?.let { currentWindow ->
                playbackPosition?.let {playbackPosition ->
                    (player as SimpleExoPlayer).seekTo(currentWindow, playbackPosition)
                }
            }



            (player as SimpleExoPlayer).prepare(mediaSource, false, false)
        }
    }

    private fun releasePlayer(){
        player?.stop()
        player?.release()
        player = null
    }

    override fun onPause() {
        super.onPause()
        if(Util.SDK_INT < 24){
            releasePlayer()
        }
    }

    override fun onStop() {
        super.onStop()
        if(Util.SDK_INT >= 24){
            releasePlayer()
        }
    }
    companion object {
        private const val VIDEO_FILE_NAME_KEY = "video_file_name"

        fun create(video: File) = VideoFragment().apply {
            arguments = Bundle().apply {
                putString(VIDEO_FILE_NAME_KEY, video.absolutePath)
            }
        }

    }
}

Моя проблема в том, что когда я переключаюсь между VideoFragment s, проводя, предыдущий Exoplayer не высвобождается должным образом, так что я все еще могу слышать аудио первого видео после запуска 2-го видео.

Хотя я добавил releasePlayer() метод для запуска проигрывателя в соответствующие методы жизненного цикла, мне кажется, что ViewPager это не волнует.

Как остановить / отпустить экземпляр ExoPlayer при использовании с ViewPager?

Ответы [ 2 ]

0 голосов
/ 29 апреля 2020

Я знаю, что уже поздно. Но, может, кто-нибудь поможет, я тоже столкнулся с той же проблемой. Решение заключается в использовании StatePagerAdapter .

    class ViewPagerAdapter extends FragmentStatePagerAdapter {

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFragment(Fragment fragment) {
        mFragmentList.add(fragment);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}

Старый, который я использовал

    class ViewPagerAdapter extends FragmentPagerAdapter {

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFragment(Fragment fragment) {
        mFragmentList.add(fragment);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
} 

Разница на родителя Классы

FragmentStatePagerAdapter <== использовать это сейчас </p>

 class ViewPagerAdapter extends FragmentStatePagerAdapter {

public ViewPagerAdapter(FragmentManager manager) {
    super(manager, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}

FragmentPagerAdapter <== Я использовал </p>

class ViewPagerAdapter extends FragmentPagerAdapter {

public ViewPagerAdapter(FragmentManager manager) {
    super(manager);
}

FragmentPagerAdapter это обеспечивает метод, который сейчас считается устаревшим, можно увидеть ниже:

         @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isVisibleToUser){
        if (!loaded){
            loadStatus();
        }
    }
}

это использовалось для управления ресурсами / воспроизведения / паузы на основе, если фрагмент виден пользователю в View Pager. Устаревший

Теперь Если вы хотите обрабатывать ресурсы / состояние фрагмента, вы должны использовать State Pager Adapter, как я описал выше. Реализация StatePagerAdapter вызовов onPause () и onResume () методов фрагмента на основе его видимости для пользователя.

Ответьте так на вопрос это Адаптер реализации, аналогичный StatePagerAdapter , и приостановите проигрыватель с помощью метода onPause () фрагмента и возобновите его в методе onResume ().

0 голосов
/ 14 марта 2020

С ViewPager2 ваш фрагмент может быть предварительно загружен до того, как он будет фактически отображен пользователю. Одним из способов решения вашей проблемы является прослушивание изменений страницы и releasePlayer() оттуда, а не ожидание уничтожения фрагмента. См. Документацию для ViewPager2.OnPageChangeCallback здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...