If you’re building an Android app, chances are you need video whether it’s on-demand playback, live streaming, or short video clips. This guide shows you how to add streaming to your Android application with the right tools and protocols.
In Android development, you’ll typically work with two types of video streaming:
Recommended streaming protocols for android
For most Android apps, HLS or MPEG-DASH are the best options for reliable, scalable video streaming. Both work seamlessly with ExoPlayer and handle variable network conditions effectively.
Before integrating video streaming into your Android app, it’s important to choose the right player. Android doesn’t offer true “native adaptive streaming” out of the box — instead, you get playback libraries that handle media decoding and rendering.
The two main options:
1. MediaPlayer (Android SDK)
When to use: Only for simple, lightweight use cases with basic MP4 or HLS playback. Not recommended for production-grade streaming apps.
2. ExoPlayer (Recommended)
When to use: Ideal for any serious production app that needs reliable, flexible, and scalable video playback.
Step 1: Add ExoPlayer Dependencies
In your app-level build.gradle:
groovy
1dependencies {
2 implementation 'com.google.android.exoplayer:exoplayer:2.19.1'
3}
4
Sync your project after adding the dependency.
Step 2: Add playerview to your layout
In your activity_main.xml (or fragment layout):
xml
1<com.google.android.exoplayer2.ui.PlayerView
2 android:id="@+id/playerView"
3 android:layout_width="match_parent"
4 android:layout_height="250dp"
5 android:background="@android:color/black" />
6
PlayerView handles video rendering and includes built-in playback controls like play, pause, and seek.
Step 3: Initialize ExoPlayer and play video
Java example:
1PlayerView playerView;
2ExoPlayer exoPlayer;
3
4@Override
5protected void onCreate(Bundle savedInstanceState) {
6 super.onCreate(savedInstanceState);
7 setContentView(R.layout.activity_main);
8
9 playerView = findViewById(R.id.playerView);
10 exoPlayer = new ExoPlayer.Builder(this).build();
11 playerView.setPlayer(exoPlayer);
12
13 MediaItem mediaItem = MediaItem.fromUri("https://your-server.com/your-video.mp4");
14 exoPlayer.setMediaItem(mediaItem);
15 exoPlayer.prepare();
16 exoPlayer.play();
17}
18
19@Override
20protected void onDestroy() {
21 super.onDestroy();
22 if (exoPlayer != null) {
23 exoPlayer.release();
24 }
25}
26
Kotlin example:
private lateinit var playerView: PlayerView
private lateinit var exoPlayer: ExoPlayer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
playerView = findViewById(R.id.playerView)
exoPlayer = ExoPlayer.Builder(this).build()
playerView.player = exoPlayer
val mediaItem = MediaItem.fromUri("https://your-server.com/your-video.mp4")
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.playWhenReady = true
}
override fun onDestroy() {
super.onDestroy()
exoPlayer.release()
}
If you care about smooth playback, adaptive streaming, and reliable error handling on Android especially for live or long-form video ExoPlayer is the right tool for the job.
Optional enhancements (Consider adding later)
If your video source is an HLS (.m3u8) or MPEG-DASH stream, ExoPlayer handles adaptive bitrate switching automatically. No extra configuration is required — just provide the streaming URL:
kotlin
1val mediaItem = MediaItem.fromUri("https://your-server.com/stream/playlist.m3u8")
2exoPlayer.setMediaItem(mediaItem)
3exoPlayer.prepare()
4exoPlayer.playWhenReady = true
5
ExoPlayer adjusts video quality in real time based on network conditions, providing smoother playback on both fast and unstable connections.
For live streams, use the same approach. If your backend supports HLS or DASH, ExoPlayer can handle the live playback, including adaptive bitrate and low-latency optimizations.
Example for live HLS:
Kotlin
val liveStreamUrl = "https://your-server.com/live/stream.m3u8"
val mediaItem = MediaItem.Builder()
.setUri(liveStreamUrl)
.setLiveConfiguration(
MediaItem.LiveConfiguration.Builder()
.setMaxPlaybackSpeed(1.02f) // Slight speed-up to stay closer to real-time
.build()
)
.build()
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.playWhenReady = true
The LiveConfiguration helps ExoPlayer handle live-specific playback optimizations, like reducing latency and maintaining sync with the live edge.
Getting streaming to work is one thing making sure it performs well across devices and networks is where production-ready apps stand out. Here are key practices for improving playback quality in Android:
ExoPlayer supports SimpleCache to locally cache video chunks. This helps reduce rebuffering, especially on unstable networks.
Always listen for playback errors to catch issues like network drops or invalid streams:
Kotlin
1exoPlayer.addListener(object : Player.Listener {
2 override fun onPlayerError(error: PlaybackException) {
3 Toast.makeText(this@MainActivity, "Playback error: ${error.message}", Toast.LENGTH_SHORT).show()
4 }
5})
6
Avoid memory leaks by releasing the player properly when the activity or fragment stops:
Kotlin
override fun onPause() {
super.onPause()
exoPlayer.playWhenReady = false
}
override fun onDestroy() {
super.onDestroy()
exoPlayer.release()
}
ExoPlayer supports multiple subtitle formats like VTT, SRT, and TTML. Here’s how to add subtitles:
Kotlin
val subtitle = MediaItem.SubtitleConfiguration.Builder(Uri.parse("https://your-server.com/subtitles.vtt"))
.setMimeType(MimeTypes.TEXT_VTT)
.build()
val mediaItem = MediaItem.Builder()
.setUri("https://your-server.com/video.mp4")
.setSubtitleConfigurations(listOf(subtitle))
.build()
exoPlayer.setMediaItem(mediaItem)
If your app includes paid or premium content, use Widevine DRM to prevent unauthorized access:
Kotlin
val drmConfiguration = MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
.setLicenseUri("https://your-license-server.com")
.build()
val mediaItem = MediaItem.Builder()
.setUri("https://your-server.com/protected-content.m3u8")
.setDrmConfiguration(drmConfiguration)
.build()
exoPlayer.setMediaItem(mediaItem)
Backend considerations for better streaming
Great playback also depends on your backend setup not just your player code.
Platforms like FastPix can handle these complexities for you from adaptive encoding to live-to-VOD recording and content protection so you can focus on your Android app instead of managing video pipelines or CDN workflows.
Testing your streaming setup
Don’t forget to test across:
Tools like Android Profiler and Charles Proxy can help analyze network calls and media buffering.
If you’re building serious video streaming into your Android app, basic playback isn’t enough. Production-ready apps need flexibility, security, and reliable delivery at scale.
In this section, we’ll cover:
ExoPlayer is more than a plug-and-play player it’s built for customization.
Custom playback controls
The default PlayerView gives you standard controls, but ExoPlayer lets you fully replace the UI.
Example: Custom play button with manual control:
xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/customPlayerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:use_controller="false" />
<ImageButton
android:id="@+id/playButton"
android:src="@drawable/ic_play"
android:layout_centerInParent="true"
android:background="?attr/selectableItemBackgroundBorderless"/>
</RelativeLayout>
kotlin
CopyEdit
playButton.setOnClickListener {
if (exoPlayer.isPlaying) {
exoPlayer.pause()
} else {
exoPlayer.play()
}
}
ExoPlayer supports speed adjustments out of the box:
Kotlin
exoPlayer.setPlaybackParameters(PlaybackParameters(1.5f))
You can wire this to a UI dropdown or slider for dynamic user control.
Enable PiP to keep video playback running when users switch apps:
kotlin
override fun onUserLeaveHint() {
val params = PictureInPictureParams.Builder().build()
enterPictureInPictureMode(params)
}
For apps that monetize video or stream licensed content, DRM is non-negotiable. ExoPlayer supports Widevine DRM, Google’s content protection system.
Kotlin
val drmConfiguration = MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
.setLicenseUri("https://license-server.com/getlicense")
.build()
val mediaItem = MediaItem.Builder()
.setUri("https://yourserver.com/encrypted/video.mpd")
.setDrmConfiguration(drmConfiguration)
.build()
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()
The license server handles authentication and delivers the decryption keys securely.
For subscription models or download-to-watch apps:
This reduces server load and ensures secure offline viewing.
Your playback experience depends as much on the backend as on the app. Here’s what to get right on the server side:
For Android apps, HLS or DASH are the most scalable options.
CDNs like, AWS CloudFront, or FastPix cache content closer to your users. This cuts down:
Your video manifest (.m3u8 for HLS, .mpd for DASH) should list multiple resolutions, for example:
ExoPlayer automatically selects the best stream based on the user’s network bandwidth.
For live video, your backend needs:
If you want to skip server management, FastPix supports:
This removes the need to handle segmenting, packaging, or scaling infrastructure yourself.
For production-grade video on Android:
If you don’t want to spend time managing encoders, packaging workflows, or CDN configurations, FastPix handles the entire streaming stack for you from just-in-time encoding and adaptive bitrate packaging to live-to-VOD recording and content protection.
With support for HLS and MPEG-DASH out of the box, FastPix lets you stream on Android and iOS without the overhead of building and scaling your own video infrastructure. You focus on your app and user experience FastPix takes care of the video delivery. Go through our Docs and Guides to understand FastPix better or talk to us to see how we can help with your workflow.
ExoPlayer supports segment-level caching using SimpleCache, which allows you to store video chunks locally. This helps reduce rebuffering during playback, especially under fluctuating network conditions. You can integrate CacheDataSourceFactory and configure cache size and eviction policies to control performance.
Yes, ExoPlayer supports adaptive bitrate (ABR) streaming for live content. If your HLS or DASH stream includes multiple quality renditions, ExoPlayer will automatically switch between them based on real-time bandwidth conditions — ensuring minimal buffering and optimal quality.
MediaItem.LiveConfiguration is designed for live stream tuning. It enables features like low-latency playback and playback speed adjustments to stay close to the live edge. Regular playback settings don’t optimize for real-time latency or sync in the same way.
For Android in 2025, ExoPlayer remains the top choice due to its support for adaptive streaming, DRM, and customization. Unlike MediaPlayer, it’s actively maintained by Google, handles modern formats like HLS/DASH, and integrates with analytics, subtitles, and offline playback.
To stream high-quality video without buffering, use adaptive bitrate streaming with ExoPlayer, deliver content via a CDN, and segment videos into smaller chunks. Caching, error handling, and real-time bitrate switching are key to smooth playback across devices.