Devesh Rx Logo
Devesh Rx Blog

Android Media3 ExoPlayer: Jetpack Compose Video Guide

July 13, 2026

Android Media3 ExoPlayer: Jetpack Compose Video Guide

Building a custom video player in Android often feels needlessly complex. Legacy libraries cause memory leaks, create lifecycle headaches, and refuse to cooperate with modern UI toolkits. You end up wasting hours debugging hardware decoders instead of shipping features.

The solution is using the Media3 ExoPlayer Android library alongside Jetpack Compose. This guide walks you through building a modern, seamless video playback experience for both network streams and local files.

Why Use Media3 ExoPlayer for Android Video Streaming?

Media3 is the latest media library from Google, succeeding the original ExoPlayer and MediaCompat libraries. It simplifies the integration between the underlying playback engine and your app’s UI.

Before diving into the code, you need to understand three core concepts:

  • ExoPlayer: The engine that handles media loading, buffering, and decoding.
  • PlayerView: The UI component that renders the video and provides standard playback controls.
  • MediaItem: A representation of your media source (URL or local file) and its metadata.

Project Setup: Adding Media3 Dependencies

Setting up your project correctly ensures your video player runs smoothly across all Android versions.

Gradle Dependencies for ExoPlayer

Add the following to your build.gradle.kts (Module: app) file. For this tutorial, we are using Media3 version 1.5.1.

dependencies {
    // Core ExoPlayer functionality
    implementation("androidx.media3:media3-exoplayer:1.5.1")
    // UI components like PlayerView
    implementation("androidx.media3:media3-ui:1.5.1")
    // Shared media constants and classes
    implementation("androidx.media3:media3-common:1.5.1")
}

AndroidManifest Permissions for Streaming

To stream video Android apps require the INTERNET permission. If you plan to play local files, you must also request storage permissions.

<!-- AndroidManifest.xml -->
<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

    <application
        ...
        android:usesCleartextTraffic="true"> <!-- Needed for http:// (non-https) streams -->
        ...
    </application>
</manifest>

Building the Video Player in Jetpack Compose

Integrating an ExoPlayer Jetpack Compose architecture requires bridging traditional Android Views with composables.

Initializing the ExoPlayer Instance

The player must be tied to your Activity’s lifecycle. Initialize it in onCreate and always release it in onDestroy to free up hardware decoders and prevent memory leaks.

class MainActivity : ComponentActivity() {
    lateinit var player: ExoPlayer

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

        // 1. Initialize the player
        player = ExoPlayer.Builder(this).build()

        setContent {
            // UI Code here
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 2. Always release the player to prevent memory leaks
        player.release()
    }
}

Bridging PlayerView with AndroidView

Because PlayerView is a traditional Android View, we use AndroidView to host it inside our Compose UI. This creates a reliable Compose bridge for your video surface.

@Composable
fun VideoPlayerSurface(
    player: Player?,
    modifier: Modifier = Modifier
) {
    AndroidView(
        factory = { context ->
            PlayerView(context).apply {
                this.player = player
                // Configure UI settings here
                this.useController = true
            }
        },
        update = { view ->
            // Keep the view in sync with the player state
            view.player = player
        },
        modifier = modifier
    )
}

How to Stream Video from a URL in Android

Playing a network stream is straightforward. Create a MediaItem from your URI, set it to the player, and call prepare().

fun playFromUrl(url: String) {
    val mediaItem = MediaItem.fromUri(url)
    player.setMediaItem(mediaItem)
    player.prepare() // Player starts buffering the video stream
    player.play()    // Player starts visuals
}

How to Play Local Video Files

If you want users to pick a video from their gallery, use the modern ActivityResultContracts . This handles the local file playback securely without requiring broad storage access.

val filePickerLauncher = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.GetContent()
) { uri: Uri? ->
    uri?.let {
        player.setMediaItem(MediaItem.fromUri(it))
        player.prepare()
        player.play()
    }
}

// Trigger in UI
Button(onClick = { filePickerLauncher.launch("video/*") }) {
    Text("Select Local Video")
}

Best Practices for Android Video Playback

To ensure your media3 library setup performs optimally, keep these key takeaways in mind:

  • Lifecycle Management: Always call player.release() in onDestroy.
  • Cleartext Traffic: Enable android:usesCleartextTraffic="true" in the manifest if your video URLs are not https.
  • Aspect Ratio: Use Modifier.aspectRatio(16f / 9f) on your VideoPlayerSurface to ensure a consistent video box.
  • Prepare then Play: Always call player.prepare() before player.play() to ensure the engine starts loading the stream early.

FAQ: Media3 ExoPlayer in Jetpack Compose

What is the difference between ExoPlayer and Media3? Media3 is the newest media library suite from Google. ExoPlayer is the playback engine within Media3. Moving to Media3 standardizes the API and integrates better with modern Android components.

How do I release an ExoPlayer in Jetpack Compose? You should release the ExoPlayer by calling player.release() in the onDestroy() lifecycle method of your hosting Activity or Fragment. This frees up hardware decoders and prevents memory leaks.

Does ExoPlayer support Jetpack Compose natively? Media3 does not have native Compose components yet. You bridge this gap by wrapping the traditional PlayerView inside an AndroidView composable function.


📌 Full Course Playlist https://www.youtube.com/playlist?list=PLO1OrQEU0vHNmD9Xqzs-qXwzzwrDvdhVu

#Tutorial
0 Introduction
1 Setting up Android Studio IDE
2 Mastering Android Studio: Navigating the IDE & Project Structure
3 Android Activity & Lifecycle Explained
4 Android Services: Background, Foreground, and Bound Services Explained
5 Android Broadcast Receivers: The Complete Guide to Listening and Sending Events
6 Android Content Provider API Tutorial: Access User Data Safely (Kotlin)
7 How to Build UI with Jetpack Compose: A Beginner’s Guide
8 Android Runtime Permissions in Kotlin and Jetpack Compose: Step-by-Step Guide
9 Android Intents Guide: Master Screen Navigation and Data Sharing
10 Android Room Database: Complete CRUD Tutorial with Kotlin
11 Android Internal Storage: File I/O Tutorial
12 Android MediaStore API Tutorial: How to Save and Read Files
13 Master Storage Access Framework in Jetpack Compose
14 How to Create Android Notifications with Jetpack Compose & Kotlin
15 How to Use SharedPreferences in Android (Kotlin & Compose)
16 OkHttp Android Tutorial: Complete Kotlin Guide
17 Android MediaPlayer API: Build a Robust Audio Player
18 Android Media3 ExoPlayer: Jetpack Compose Video Guide

~ ~ THANK YOU FOR READING ~ ~

Share: