How do I use MVVM with bound service?

818 views Asked by At

I have a bound service that is started as foreground service for handling exoplayer.

Here is how my fragment handles the service -

@AndroidEntryPoint
class AudioPlayerFragment: Fragment() {

    private var binding: AudioPlayerFragmentBinding by autoCleared()

    private lateinit var podcast: Podcast

    private lateinit var podcastPlayerService: PodcastPlayerService
    private var isBound = false

    private val connection = object: ServiceConnection {

        override fun onServiceConnected(p0: ComponentName?, iBinder: IBinder?) {

            val binder = iBinder as PodcastPlayerService.LocalBinder
            podcastPlayerService = binder.getService()
            isBound = true

            initPlayer()
        }

        override fun onServiceDisconnected(p0: ComponentName?) {

            isBound = false
        }
    }

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

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

        podcast = arguments?.getParcelable(ExtrasKeyAndValues.KEY_PODCAST)!!
        binding.title.text = podcast.title
        binding.description.text = podcast.description
        startPodcastPlayerService(podcast)

        binding.exoPlayerView.useController = true
        binding.exoPlayerView.showController()
        binding.exoPlayerView.controllerAutoShow = true
        binding.exoPlayerView.controllerHideOnTouch = false
    }

    override fun onStart() {
        super.onStart()

        Intent(activity, PodcastPlayerService::class.java).also {
                intent -> activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
        initPlayer()
    }

    override fun onStop() {

        activity?.unbindService(connection)
        isBound = false

        super.onStop()
    }

    private fun initPlayer() {
        if (isBound) {
            val player: SimpleExoPlayer = podcastPlayerService.getPlayerInstance()
            binding.exoPlayerView.player = player
        }
    }

    private fun startPodcastPlayerService(podcast: Podcast) {
        val intent = Intent(context, PodcastPlayerService::class.java)
        val serviceBundle = Bundle()
        serviceBundle.putParcelable(ExtrasKeyAndValues.KEY_PODCAST, podcast)
        intent.putExtra(ExtrasKeyAndValues.BUNDLE_PODCAST_SERVICE, serviceBundle)
        context?.let { Util.startForegroundService(it, intent) }
    }
}

The problem is that the service (obviously) restarts when the phone is rotated (config change). How do I architect my app in a way that the service is not restarted but just attaches itself to the fragment and its UI?

Placing it in a ViewModel does not make sense because it is recommended to avoid putting android framework related stuff in there.

1

There are 1 answers

2
Bo Z On BEST ANSWER

The best thing here to create something like VideoHelper class that will include all player related code and initialization. Includes the function that will control the state of the player and other related things. Referring to your question service is not restarted but just attaches itself to the fragment and its UI as I remember I implemented the logic that saved the state and progress of the video in onPause or onStop and set it back when onResume. Basically, you need to create in separate VideoHelper functions like getProgress and call it in onPause or onStop and function setProgress that you should call from onResume and before setting it check if it not empty or null. The progress you can save any way you want - shared preferences or some static data or anything else.