Integrating ExoPlayer for Video Playback in Android Application
MediaPlayer from the standard Android library handles local files and simple HTTP links. Once you have HLS streaming, DASH manifest, DRM protection via Widevine, or need to seek in a stream without buffering from the beginning — MediaPlayer reaches its limits and ExoPlayer begins.
ExoPlayer (with Media3 — androidx.media3:media3-exoplayer) is Google's library for media playback on Android. It's used in YouTube, Google TV and most streaming applications on the platform.
What We're Integrating
Basic integration: ExoPlayer + PlayerView in layout, loading MediaItem by URL, managing playback through Player.Listener. This takes several hours.
Real tasks are more complex. Adaptive streaming (HLS/DASH): HlsMediaSource or DashMediaSource with DefaultDataSource.Factory. ExoPlayer automatically selects quality based on AdaptiveTrackSelection — but you need to properly configure DefaultBandwidthMeter and DefaultLoadControl with buffering parameters (minBufferMs, maxBufferMs, bufferForPlaybackMs). Incorrect parameters lead to constant buffering pauses even on fast Internet.
DRM via Widevine: DefaultDrmSessionManager with HttpMediaDrmCallback, which accesses the license server. Widevine L1 certificate works only on devices with hardware DRM environment (TEE). L3 is software-based, available on all devices, but quality is limited. The manifest needs android:requestLegacyExternalStorage for some old devices with filesystem quirks.
Picture-in-Picture: when transitioning to background mode, ExoPlayer should continue playback. Activity switches to PiP mode via enterPictureInPictureMode() with PictureInPictureParams. Key — Player is not directly bound to Activity lifecycle, otherwise it stops at onStop(). Solution: ExoPlayer in MediaSessionService or at least a properly lifecycle-aware component.
Typical Issues
ExoPlayer on PlayerView inside RecyclerView — classic pain. On fast scrolling, multiple PlayerViews try to play media simultaneously. You need "one active player" logic with pausing the previous one when the next appears. RecyclerView.OnScrollListener + LinearLayoutManager.findFirstCompletelyVisibleItemPosition() — standard approach.
Audio focus: if the application doesn't request AudioFocus and doesn't respond to its loss, the user's music player won't pause. AudioFocusRequest with OnAudioFocusChangeListener — mandatory part of any video player.
Transitioning between screens without interrupting playback: ExoPlayer should be stored above the screen's lifecycle — in ViewModel or service singleton. PlayerView.setPlayer(null) when leaving screen, PlayerView.setPlayer(player) when returning. Without this you get black screen or double audio.
Versioning: Media3 1.3.x is the current branch at time of writing. Old com.google.android.exoplayer2 is deprecated; migration to androidx.media3 is part of integration work in existing projects.
Integration timeline: 2–3 days for basic player with HLS and playback management. Widevine DRM, PiP, player in RecyclerView — each adds 1–2 days. Cost is calculated individually.







