Post

Icon AndroidMediaPlayer Queue Playback Fix

Fixed queue playback issue where only the first track was playing. Updated track index tracking and added proper callbacks for onTrackStarted during media item transitions to ensure all tracks in a queue play sequentially.

AndroidMediaPlayer Queue Playback Fix

Problem

When passing 3 tracks to playQueue(), only the first track was playing. After the first track completed, the queue wasn’t continuing to play tracks 2 and 3, and onQueueCompleted() was being called immediately.

Root Cause

The original implementation had several issues with track index tracking:

  1. Initial index was 0: Starting with currentTrackIndex = 0 meant the first track would match immediately on STATE_READY, but this index wasn’t being properly managed for subsequent tracks.

  2. onMediaItemTransition wasn’t calling onTrackStarted: When ExoPlayer transitioned to the next track, we were incrementing the index but not notifying the listener that a new track started. This meant tracks 2 and 3 never got their start callbacks.

  3. Race condition in state tracking: The logic in onPlaybackStateChanged and onMediaItemTransition wasn’t coordinated, leading to missed callbacks.

Solution

The fix involves:

  1. Start with index -1: This ensures we can detect the first track properly when it becomes ready.

  2. Add hasNotifiedTrackStart flag: This prevents duplicate notifications and helps track whether we’ve notified about the current track.

  3. Call onTrackStarted in both places:
    • In onPlaybackStateChanged when STATE_READY is reached (for the first track)
    • In onMediaItemTransition when transitioning between tracks (for subsequent tracks)
  4. Use player.currentMediaItemIndex: Instead of manually incrementing, we rely on ExoPlayer’s own index tracking to know which track is currently playing.

  5. Better logging: Added detailed logging to track state changes and transitions.

Key Changes

Before

1
2
3
4
5
6
7
8
9
10
11
private var currentTrackIndex = 0

override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
    if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
        // Only notified completion, not start of new track
        if (currentTrackIndex > 0) {
            currentListener?.onTrackCompleted(currentTrackIndex - 1)
        }
        currentTrackIndex++  // Just increment, no start callback
    }
}

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private var currentTrackIndex = -1
private var hasNotifiedTrackStart = false

override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
    if (mediaItem != null && reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
        // Notify completion of previous track
        if (currentTrackIndex >= 0 && hasNotifiedTrackStart) {
            currentListener?.onTrackCompleted(currentTrackIndex)
        }
        
        // Start new track
        val newIndex = player.currentMediaItemIndex
        if (newIndex >= 0 && newIndex < trackInfoList.size) {
            currentTrackIndex = newIndex
            val trackInfo = trackInfoList[currentTrackIndex]
            currentListener?.onTrackStarted(currentTrackIndex, trackInfo)  // NOW CALLED!
            hasNotifiedTrackStart = true
        }
    }
}

Expected Behavior Now

With 3 tracks queued, you should see:

  1. Track 0 startsonTrackStarted(0, trackInfo0)
  2. Track 0 completesonTrackCompleted(0)
  3. Track 1 startsonTrackStarted(1, trackInfo1)
  4. Track 1 completesonTrackCompleted(1)
  5. Track 2 startsonTrackStarted(2, trackInfo2)
  6. Track 2 completesonTrackCompleted(2)
  7. Queue completesonQueueCompleted()

Testing

Build and run the app again. The logs should now show:

  • “Playback state changed” messages with proper state tracking
  • “Media item transition” messages when moving between tracks
  • All onTrackStarted and onTrackCompleted callbacks for each of the 3 tracks
  • onQueueCompleted only after all tracks have played
This post is licensed under CC BY 4.0 by the author.