How to make simple music player in Android?

In this tutorial, we’ll build a simple music player app for Android. We’ll cover how to request audio permissions, retrieve audio files, implement playback controls like play/pause, handle track completion, and save/restore state. By the end, you’ll have a basic but fully-functional music player app.

To follow along, you’ll need to have Android Studio installed along with the latest Android SDK. We’ll be using Kotlin to build the app, so familiarity with Kotlin syntax will be helpful. We’ll also be working with some core Android frameworks like MediaPlayer, so basic Android development experience will also be useful.

Design the UI

The starting point for designing the UI of a simple music player in Android is to use a RelativeLayout as the root view group. RelativeLayout allows you to position child views relative to each other and the parent layout. This provides the flexibility needed to design the UI (Feel the Beat. UI Design for Music Streaming Services).

You’ll also want to add a MediaController for playback controls like play/pause, next, previous etc. The MediaController provides these built-in controls and handles interacting with the MediaPlayer (Designing a Seamless UI/UX for Music Streaming Apps).

Other common UI elements to design include Buttons for actions, TextViews for labels, SeekBar for track progress and more. Position these elements as needed within the RelativeLayout. For example, Buttons on the bottom, SeekBar between them, TextViews at the top etc. This provides an intuitive music player UI for users (Buttons, Sliders, and Keys – A Survey on Musical Grid).

Request Audio Permissions

In order to access audio files on the device, we need to request the READ_EXTERNAL_STORAGE permission at runtime. This is because starting in Android 6.0 (API level 23), dangerous permissions like reading external storage are not granted at install time. Instead, they must be requested at runtime.

Here’s how to request the READ_EXTERNAL_STORAGE permission:

if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE) 
    != PackageManager.PERMISSION_GRANTED) {

  // Permission is not granted, request it
  ActivityCompat.requestPermissions(thisActivity,
      new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
      PERMISSION_REQUEST_CODE);

}

This will prompt the user with a dialog asking for permission. The result will be delivered to the onRequestPermissionsResult() callback where we can proceed if permission is granted.

Without this permission, we won’t be able to access the user’s audio files to load them into our media player. Requesting dangerous permissions at runtime helps protect user privacy by only granting access when the user allows it.

Get Audio Files

To get a list of audio files available on the device, we can use the MediaStore API. The MediaStore contains metadata for all media files on the device.

First, we need to query the MediaStore to get a cursor containing all the audio files. We can use a query like this:


val audioUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Audio.Media._ID, 
                        MediaStore.Audio.Media.TITLE,
                        MediaStore.Audio.Media.DATA,
                        MediaStore.Audio.Media.ARTIST,
                        MediaStore.Audio.Media.ALBUM)
val selection = MediaStore.Audio.Media.IS_MUSIC + " != 0"
val sortOrder = MediaStore.Audio.Media.TITLE + " ASC" 

val cursor = context.contentResolver.query(audioUri, projection, selection, null, sortOrder)

This will return a cursor with the ID, title, path, artist and album for all audio files on the device.

Then, we can display these audio files in a RecyclerView by creating a custom adapter. The adapter can take in the cursor, read the metadata from each row, and populate the views to display the title, artist, etc. This allows us to nicely display the list of audio files to the user.

Create MediaPlayer

The MediaPlayer class in Android allows applications to play audio and video files. It handles decoding audio and video files into playable streams.

To use MediaPlayer, we first need to instantiate it:


MediaPlayer mediaPlayer = new MediaPlayer();

We can then set the audio data source using the setDataSource() method. This accepts a file path URI or resource id for our audio file. For example:


mediaPlayer.setDataSource(this, Uri.parse("file:///sdcard/music.mp3"));

Or if placing the audio file in our raw resources folder:

  
mediaPlayer.setDataSource(getApplicationContext(), R.raw.music);

This initializes the MediaPlayer instance and prepares it for playback.

Implement Playback Controls

The playback controls allow the user to play, pause, skip between tracks, and adjust the volume. We will need to implement buttons to toggle playback, skip to the next/previous track, and adjust the volume.

For the play/pause button, we can attach an onClick listener that calls mediaPlayer.start() when paused and mediaPlayer.pause() when playing to toggle playback. We can store a boolean variable isPlaying to track the current playback state. When isPlaying is true, set the button text to “Pause” and when false, set it to “Play”.

For next and previous buttons, we need to keep track of the current playing index in our list of audio files. On click, increment or decrement the index, check that it is within bounds, stop the current playback, reset the media player, and call mediaPlayer.start() to begin playing the new track.

To enable volume controls, we can attach an onChange listener to a SeekBar view. In the listener, call mediaPlayer.setVolume(progress) to update the volume level based on the seekbar progress. This allows adjustable volume between 0 and max volume.

By implementing these playback controls, we allow easy control over playback of audio files within the app. Users can play/pause, skip tracks, and adjust volume as needed during music playback (Developer Android, 2022).

Update Seekbar

We need to connect the Seekbar to the MediaPlayer so that it shows and controls the playback progress. To do this, we can use the MediaPlayer callback methods onProgressUpdate and onCompletion.

In onProgressUpdate, we get the current position of the MediaPlayer and update the Seekbar’s progress to match it. We need to call this periodically to keep the Seekbar in sync.

In onCompletion, we set the Seekbar back to 0 to indicate the audio has finished playing.

By updating the Seekbar as the audio plays, we allow the user to visually see the playback progress. They can also drag the Seekbar to skip ahead or go back in the audio.

Handle Completion

To detect when audio playback completes in Android, you can use the MediaPlayer class and add a CompletionListener to be notified when the current audio finishes playing. In the onCompletion() callback, you can automatically start playing the next audio file in your playlist.

For example:

mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
  @Override
  public void onCompletion(MediaPlayer mp) {
    // Play next audio
  }
});

This allows smooth, automated playback through your audio playlist without needing to manually detect when each one finishes.

Save and Restore State

When the screen orientation changes in Android, the Activity is destroyed and recreated by default. This will reset our simple media player and stop any audio playback. To persist the MediaPlayer and playback state, we need to override the Activity lifecycle methods to save and restore the instance state.

In the Activity’s onSaveInstanceState() method, we can save the MediaPlayer instance and current playback position to the saved instance state Bundle like this:


@Override
protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  
  outState.putParcelable("media_player", mediaPlayer);
  outState.putInt("playback_position", mediaPlayer.getCurrentPosition());
}

Then in onCreate() we can restore the MediaPlayer instance and position from the Bundle:


@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  if (savedInstanceState != null) {
    mediaPlayer = savedInstanceState.getParcelable("media_player");
    int position = savedInstanceState.getInt("playback_position");
    mediaPlayer.seekTo(position);
  } 
}  

This will allow our music playback to persist across configuration changes like screen rotations. The same approach can be used to save any important state that needs to be restored when the Activity is recreated.

Sources:

https://developer.android.com/guide/topics/resources/runtime-changes

http://code.hootsuite.com/orientation-changes-on-android/

Conclusion

In this article, we walked through the steps to create a simple music player app in Android. We started by designing the UI layout in XML, then requesting audio permissions at runtime. We loaded audio files from external storage into a MediaPlayer instance and implemented playback controls like play/pause, seekbar, and next/previous track. To improve the user experience, we also saved and restored the playback state to resume playback after configuration changes.

This covers the basics of building a music app in Android. Some possible enhancements could include adding playlists, equalizers, recommendations, streaming integration, and custom audio effects. The MediaPlayer and MediaSession APIs provide many additional capabilities to take the music experience further. The full code for this simple music player is available on GitHub for anyone to use as a starting point for their own audio app experiments.

Leave a Reply

Your email address will not be published. Required fields are marked *