Building a simple audio app in Android (Part 3/3)
Synchronizing with the SeekBar
Introduction
The goal of this series of articles is to get you started with the Android MediaPlayer
API by going thru the journey of creating a very basic audio playback application called “A Simple MediaPlayer” app. This is the last part of a 3 part series that includes:
- MediaPlayer introduction
- Building the app
- Synchronizing with the SeekBar (this article)
Part 3/3 — Synchronizing with the SeekBar
This article will cover the following:
- How to synchronize the audio playback position, and duration with a
SeekBar
. - How to use the
SeekBar
to jump to different time positions in the audio during playback.
What does the app look like, and where is the source code?
Please checkout Part 2 of the series to see the app in action, and get the source on GitHub.
Synchronize with Seekbar
The app’s UI also has a SeekBar
. This does 2 things:
- It provides visual feedback to the user as media playback is progressing (by moving the scrubber in the
SeekBar
forwards as audio plays). - Allows the user to jump to any specific time location in the media by touching and dragging the scrubber. This ends up calling
seekTo()
in theMediaPlayer
to change its audio playback position.
1. Scrubber updating the UI to reflect playback position — PlaybackInfoListener interface
In order for the MediaPlayerHolder
to provide updates to the UI (in MainActivity
) that the playback position is progressing, the PlaybackInfoListener
interface is used. The MainActivity
implements this callback. It allows the MediaPlayerHolder
and MainActivity
to be decoupled.
- The
MediaPlayerHolder
reports its progress and duration, without concerning itself with how the UI displays this information. - The
MediaPlayerHolder
also reports its current state (which can bePLAYING
,PAUSED
,RESET
,COMPLETED
). - The
MainActivity
takes care of displaying the duration and progress in the UI via theSeekBar
.
1.1. Setting the SeekBar max to match the playback duration
The SeekBar
is used to display a range of values along a horizontal line. To configure the SeekBar
(which is the MainActivity
) we need to tell it what its maximum value should be. In our simple app, we will set the maximum value to the duration of our audio track in milliseconds. This happens in the MediaPlayerHolder
as follows after the MediaPlayer
is created, and the MP3 file is loaded.
1.2. Provide visual feedback as audio playback progresses
As the audio playback progresses, the SeekBar’s
setProgress(time)
method has to be called with the amount of time that has elapsed during playback. There’s no listener that can be attached to the MediaPlayer
to get this information, so we have to poll. There are 2 strategies for polling — using Handler
, or Executor
. This app uses an Executor
as it makes the code easier to read.
An SingleThreadScheduledExecutor
dispatches a Runnable
to execute every 500 ms (PLAYBACK_POSITION_REFRESH_INTERVAL_MS
). This means that at this regular interval this task is executed which paints the UI with the current position of MediaPlayer
position. You can try changing this to different numbers and press the PLAY
button to see how this changes the UI behavior.
2. User moving the scrubber — PlayerAdapter interface
In order to change the playback position you can use the the PlayerAdapter
interface. The MediaPlayerHolder
implements this interface, and here’s what it looks like partially.
2.1. Jump to any position in the media using seekTo
While the app is playing audio, if the user drags the scrubber in the SeekBar
(in MainActivity
), two things have to happen:
- The
Executor
that was running to provide playback progress updates has to be terminated. Otherwise it will move the scrubber and fight with the user as they are trying to move the scrubber themselves. - Once, the user completes dragging the scrubber, the position must be reported to the
MediaPlayer
usingseekTo()
.
Methods to handle both of these situations are provided in the PlayerAdapter
interface, which is implemented by MediaPlayerHolder
.
Here’s the code from MainActivity
where the case of the user moving the scrubber is handled. Note: mPlayerAdapter
is of type PlayerAdapter
, which is an interface implemented by MediaPlayerHolder
.
State Machine
The MediaPlayer
has a sophisticated state machine. You don’t need to fully understand it to get started. For deeper integration into MediaPlayer’s
capabilities, please refer to the state machine diagram on developers.android.com.