Skip to main content

How to Make Exoplayer Lifecycle Aware in Jetpack Compose

·3 mins

In my last article “Implementing Video Playback on Android Jetpack Compose” I have explained how to play videos with ExoPlayer in Jetpack Compose on Android.

https://itnext.io/implementing-video-playback-on-android-jetpack-compose-f73b437560ea

A reader observed the following behaviour: When putting the example app into the background it was still playing. He asked the question how to make ExoPlayer lifecycle aware so that the video stops when the app is being suspended.

Let’s recap the code from the last article, which is a @Composable that holds the ExoPlayer inside an AndroidView:

@Composable
fun VideoView(videoUri: String) {
    val context = LocalContext.current

    val exoPlayer = ExoPlayer.Builder(LocalContext.current)
        .build()
        .also { exoPlayer ->
            val mediaItem = MediaItem.Builder()
                .setUri(videoUri)
                .build()
            exoPlayer.setMediaItem(mediaItem)
            exoPlayer.prepare()
        }

    DisposableEffect(
        AndroidView(factory = {
            StyledPlayerView(context).apply {
                player = exoPlayer
            }
        })
    ) {
        onDispose { exoPlayer.release() }
    }
}

What we need is a current instance of the LocalLifeCycleOwnerthat “tells” us about the lifecycle events that are happening. Here is the full solution right away. I will explain the details step-by-step after it.

@Composable
fun VideoView(videoUri: String) {
    val context = LocalContext.current

    val exoPlayer = ExoPlayer.Builder(LocalContext.current)
        .build()
        .also { exoPlayer ->
            val mediaItem = MediaItem.Builder()
                .setUri(videoUri)
                .build()
            exoPlayer.setMediaItem(mediaItem)
            exoPlayer.prepare()
        }

    val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)

    DisposableEffect(
        AndroidView(factory = {
            StyledPlayerView(context).apply {
                player = exoPlayer
            }
        })
    ) {
        val observer = LifecycleEventObserver { owner, event ->
            when (event) {
                Lifecycle.Event.ON_PAUSE -> {
                    exoPlayer.pause()
                }
                Lifecycle.Event.ON_RESUME -> {
                    exoPlayer.play()
                }
            }
        }
        val lifecycle = lifecycleOwner.value.lifecycle
        lifecycle.addObserver(observer)

        onDispose {
            exoPlayer.release()
            lifecycle.removeObserver(observer)
        }
    }
}

Let me give you some explanation around it.

First we get the current instance of the LifecycleOwner

val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)

Inside the scope of the DisposableEffect we first create a LifecycleEventObserver .

val observer = LifecycleEventObserver { owner, event ->
    when (event) {
        Lifecycle.Event.ON_PAUSE -> {
            exoPlayer.pause()
        }
        Lifecycle.Event.ON_RESUME -> {
            exoPlayer.play()
        }
    }
}

When the app pauses, when it goes into the background, the player also pauses. When the app is being resumed, coming back into the foreground, the player continues playing the video.

Then we add this observer to the lifecycle instance.

val lifecycle = lifecycleOwner.value.lifecycle
lifecycle.addObserver(observer)

We need to add one more line that is needed for a proper cleanup. When the app is being closed, the observer needs to be removed from the lifecycle

lifecycle.removeObserver(observer)

That’s about it! Hope this will help you with your project!

Thank you for reading!

https://twissmueller.medium.com/membership

Resources #