ExoPlayer in Jetpack Compose: Ditch the XML, Play with Compose!

Anand Jeyapal
3 min readSep 19, 2024

--

Jetpack Compose is revolutionizing Android UI development, giving us the power to create beautiful, dynamic interfaces without touching a single line of XML!

But what if you could take this even further and build a video player using ExoPlayer, all within the Compose world?

Yes, it’s possible — and I’m here to show you how! In this post, we’ll dive into the exciting world of ExoPlayer, with no XML, just pure Compose magic.

Get ready to supercharge your media apps and bring your UIs to life with seamless video playback, powered entirely by Compose!”

Four simple steps

You just need to follow the 4 simple steps as mentioned below,

  1. Add dependencies
  2. Create the player
  3. Prepare the player
  4. Dispose the player

Add Dependencies

To get started, add a dependency on the ExoPlayer, UI, and Common modules of Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-ui:1.4.1"
implementation "androidx.media3:media3-common:1.4.1"

Make sure to replace 1.4.1 with your preferred version of the library. You can refer to the release notes to see the latest version.

Create the player

With Media3, you can either use the included implementation of the Player interface, ExoPlayer, or you can build your own custom implementation.

The simplest way to create an ExoPlayer instance is as follows:

val player = ExoPlayer.Builder(context).build()

This player instance should be created in onCreate() lifecycle method of the Activity, Fragment, or Service where it lives.

You can further customize this Builder as required.

Prepare the player

This step is crucial as we are giving life to the player :).

This involves creating a mediaItem and setting it to the player. Remember not to prepare the player before it comes to the foreground.

It is recommended to prepare the player in onStart() lifecycle method of the Activity.

val mediaItem = MediaItem.Builder().setUri(videoUri)

val player = ExoPlayer.Builder(context).build().apply {
setMediaItem(mediaItem)
prepare()
}

Dispose the player

It’s important to call release() on your player to free up resources when the player is no longer needed. In Activity, it should be done in the onStop() lifecycle method.

Yes! you guessed it right. In Jetpack compose we can achieve this by implementing a DisposableEffect.

Whenever the player composable is leaving from the composition, we should release the exoplayer instance.

  val lifecycleOwner = LocalLifecycleOwner.current

// Release the player when no longer needed
DisposableEffect(
lifecycleowner,
effect = {
onDispose { player.release() }
}
)

Output

Wow! You just made an exoplayer with no XML!!. Here is the output

Compose Player

Full source code

MainActivity.kt

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ComposePlayerTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
PlayerScreen(
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}

PlayerScreen.kt


@Composable
fun PlayerScreen(modifier: Modifier = Modifier) {
val mediaItem =
MediaItem.fromUri(Uri.parse("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"))
Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
VideoPlayer(mediaItem = mediaItem)
}
}

VideoPlayer.kt


@Composable
fun VideoPlayer(mediaItem: MediaItem) {
// Create context from Compose
val context = LocalContext.current

val lifecycleOwner = LocalLifecycleOwner.current

// Create and remember an ExoPlayer instance
val exoPlayer = remember {
ExoPlayer.Builder(context).build().apply {
// Start playing the video automatically
playWhenReady = true
}
}

LaunchedEffect(exoPlayer, mediaItem) {
// Create the MediaItem
exoPlayer.addMediaItem(mediaItem)
// Prepare the player
exoPlayer.prepare()
}

val defaultPlayerView =
remember {
PlayerView(context).apply {
player = exoPlayer
// Enables playback controls (play, pause, etc.)
useController = true
}
}

// Compose the UI with ExoPlayer
// AndroidView for ExoPlayer's PlayerView
AndroidView(
factory = {
defaultPlayerView.apply {
setBackgroundColor(Color.BLACK)
}
},
modifier = Modifier.fillMaxWidth()
)

// Release ExoPlayer when no longer needed
DisposableEffect(lifecycleOwner) {
onDispose {
exoPlayer.release()
}
}
}

That’s it! You’ve now seen how easy it is to implement ExoPlayer in Jetpack Compose without touching any XML.

💡 Github Here is my project link to explore further.

If you found this guide helpful and learned something new today, I’d really appreciate your support! Don’t forget to give this post a few claps and share it with your fellow Android developers.

Your feedback and appreciation help me keep creating more content like this. Happy coding! 🚀

--

--