Hello! In this tutorial, we will see how to use services for playing background music in Android. By background music, we mean even if the activity is destroyed i.e. we press back button, the music still plays. So, firstly talking about the sample app in this project. The very first screen shows the list of .mp3 and .mp4 files which it fetches from phone memory. By clicking on any song, a player appears on top and song starts playing. A button is provided to stop and play the music as and when needed. Moreover, permission is requested in case of Android 6.0 Marshmallow and higher versions for accessing the phone memory.

Output –

Download Project- here

Download apkhere

So, let’s get started. We will understand this by dividing the project into 4 parts:

  • Creating a layout for activity.
  • Fetching and displaying the music files from Phone storage.
  • Playing background music using services.
  • Permission checks for Android Marshmallow and higher versions.

Creating a new project- MusicApp:

Open Android Studio and create a new project. Name your project whatever you like. Choose an empty activity and click finish.

Adding Dependencies to Gradle:

I  have used CardView in the layout. You may use that. For using CardView, we need to add a dependency for the same. Add the following line in build.gradle(Module: app) in dependencies block. Click ‘Sync Now’.

compile 'com.android.support:cardview-v7:25.3.1'

Adding permission in Manifest.xml:

As fetches music files from phone storage, we need to add following permission in Manifest.xml:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

1. Creating layout for activity

Here we have an activity ‘ListOfSongsActivity’. We will first create a layout for that and then we will see the layout of the list item.

activity_list_of_songs.xml-

It consists of a player displayed on top which is initially hidden but is visible when you choose on any song. Below, is a list view which will display the list of songs fetched.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="@color/colorBackground"
    tools:context="com.example.mitaly.musicapp.ListOfSongsActivity">
<!--This linear layout is the player on top which shows song name and Play/Pause button-->
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/cardView"
        android:visibility="gone"
        android:layout_margin="10dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:orientation="vertical">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:id="@+id/txtSongName"
                android:layout_margin="15dp"
                android:gravity="center"
                android:textSize="20dp"
                android:text="Song Name"/>
            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:textColor="@android:color/white"
                android:background="@color/colorPrimary"
                android:id="@+id/btnPlayStop"
                android:gravity="center"/>
        </LinearLayout>
    </android.support.v7.widget.CardView>
<!--This is the list view showing list of songs-->
    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/listView"/>

</LinearLayout>

list_item.xml-

This shows the code for the list item to be used in list view. For this, right-click layout folder in res -> New -> Layout Resource file. Name the file according to your choice. Click OK.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="8dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="File Name"
        android:layout_margin="10dp"
        android:textSize="18dp"
        android:id="@+id/textSong"/>

</LinearLayout>

2. Fetching and displaying the music files from phone storage

Bean class ‘SongObject.java’-

For this purpose, we first need a bean class ‘SongObject’ which has two attributes fileName(name of .mp3 or .mp4 file) and absolutePath( the complete path where the file is located).  We have getters and setters for those.

package com.example.mitaly.musicapp;

public class SongObject {

    String fileName;
    String absolutePath;

    public SongObject(String fileName,String absolutePath) {
        this.fileName = fileName;
        this.absolutePath=absolutePath;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getAbsolutePath() {
        return absolutePath;
    }

    public void setAbsolutePath(String absolutePath) {
        this.absolutePath = absolutePath;
    }
}

Fetching all music files-

In ListOfSongsActivity.java, we have a method void initList(String path) which is responsible for fetching all .mp3 and .mp4 files from phone storage. Objects of ‘SongObject’ class are produced and their references are stored in ‘listOfContents‘ list. This list is an input to AdapterClass which binds the data to list item. Given below is the code for the initList method.

//Fetching .mp3 and .mp4 files from phone storage
    void initList(String path){
        try {
            File file = new File(path);
            File[] filesArray=file.listFiles();
            String fileName;
            for(File file1:filesArray) {
                if (file1.isDirectory()) {
                    initList(file1.getAbsolutePath());
                } else {
                    fileName=file1.getName();
                    if ((fileName.endsWith(".mp3")) || (fileName.endsWith(".mp4"))){
                        listOfContents.add(new SongObject(file1.getName(), file1.getAbsolutePath()));
                    }

                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

Displaying list using Adapter class-

This class is mainly responsible for binding the data in list_item and then list_item to list view. There’s a method getView() which is performing all such tasks. It takes the data one by one from ‘listOfContents which we initialized earlier and binds the data to list_item. Code goes like-

public class AdapterClass extends ArrayAdapter<SongObject> {
    Context cxt;
    int res;
    ArrayList<SongObject> list;

    public AdapterClass(Context context, int resource, ArrayList<SongObject> objects) {
        super(context, resource, objects);
        cxt=context;
        res=resource;
        list=objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view=null;

        //Initializing view which will point to layout file list_item
        view= LayoutInflater.from(cxt).inflate(res,parent,false);

        //Initializing TextView
        TextView fileName=(TextView)view.findViewById(R.id.textSong);

        SongObject sdOb=list.get(position);
        //Setting the Icon and FileName
        fileName.setText(sdOb.getFileName());

        return view;
    }
}

Related:

Music Player app using MediaPlayer API in android

How to Enable/Disable GPS location services using LocationManager programmatically in Android

Uploading file to Google Drive in Android Application

Downloading File From Android WebView

Android Pick File Programatically

Dealing with views-

This code shows the initialization of views and handling of events such as button(Play/Stop) press and selecting a music from the list view. Notice, when the user selects a song from the list view, service is started. When the user presses Stop button, service is stopped. This allows music to play in the background. The service code is explained later. Comments are used for better understanding. If there’s any problem in understanding the code, feel free to ask.

public class ListOfSongsActivity extends AppCompatActivity {

    ListView listview;
    Button btnPlayStop;
    TextView txtSongName;
    CardView cardView;
    ArrayList<SongObject> listOfContents;
    AdapterClass adapter;
    String path;
    static String absolutePath, songName;
    public static boolean playing = false;

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_of_songs);
        
        // If Android Marshmello or above, then check if permission is granted
        if(Build.VERSION.SDK_INT >= 23)
            checkPermission();
        else
            initViews();
    }


    void initViews(){
        //initializing views
        btnPlayStop = (Button)findViewById(R.id.btnPlayStop);
        txtSongName = (TextView)findViewById(R.id.txtSongName);
        cardView = (CardView)findViewById(R.id.cardView);
        listview=(ListView)findViewById(R.id.listView);
        listOfContents = new ArrayList<>();

        //If music is playing already on starting the app, player should be visible with Stop button
        if(playing){
            txtSongName.setText(songName);
            cardView.setVisibility(View.VISIBLE);
            btnPlayStop.setText("Stop");
        }

        //Gives you the full path of phone memory
        path= Environment.getExternalStorageDirectory().getAbsolutePath();

        //Calling the function which fetches the list of music files
        initList(path);
        
        //initializing the adapter and passing the context, list item and list of references of SongObject
        adapter=new AdapterClass(this,R.layout.list_item,listOfContents);
        listview.setAdapter(adapter);

        //handling events when user clicks on any music file in list view
        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                //player is visible
                cardView.setVisibility(View.VISIBLE);

                //If some other song is already playing, stop the service
                if(playing){
                    Intent i =new Intent(ListOfSongsActivity.this, MusicService.class);
                    stopService(i);
                }

                playing = true;

                //getting absolute path of selected song from bean class 'SongObject'
                SongObject sdOb = listOfContents.get(position);
                absolutePath = sdOb.getAbsolutePath();

                //Play the selected song by starting the service
                Intent start = new Intent(ListOfSongsActivity.this, MusicService.class);
                startService(start);

                //Get and set the name of song in the player
                songName = listOfContents.get(position).getFileName();
                txtSongName.setText(songName);
                btnPlayStop.setText("Stop");
                }

        });

        //Handling events when button Play/Stop is clicked in the player
        btnPlayStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(playing){
                    //If song is playing and user clicks on Stop button
                    //Stop the song by calling stopService() and change boolean value
                    //text on button should be changed to 'Play'
                    playing = false;
                    btnPlayStop.setText("Play");
                    Intent i = new Intent(ListOfSongsActivity.this,MusicService.class);
                    stopService(i);
                }else if(!playing){
                    //If song is not playing and user clicks on Play button
                    //Start the song by calling startService() and change boolean value
                    //text on button should be changed to 'Stop'
                    playing = true;
                    btnPlayStop.setText("Stop");
                    Intent i = new Intent(ListOfSongsActivity.this, MusicService.class);
                    startService(i);
                }
            }
        });

    }

    //Code for initList(String path) is discussed earlier

    //Code for permission checks explained later

}

3. Playing background music using services

Now, we have arrived at the main part of the project. First, let’s understand how to create service class. Right click on package name folder, here we have ‘com.example.mitaly.musicapp’ -> New -> Service -> Service. Give appropriate name to your service class. Click Finish. This will create a class that extends Service class of java.

We have used Started service which is a type of service. There’s MediaPlayer class to access built-in media player services such as playing audio, video etc. We have used that for playing the chosen song. We have seen earlier that if the user chooses a song from the list, startService() is called. This is nothing but the onStartCommand() which gets executed. This method will initialize MediaPlayer by giving the path of the audio file for playback and then start the player. The songs start playing then. When to have studied that when the user presses Stop button of player, shown on top of activity, stopService() is called. This will make onDestroy() to be called which will stop the playback.

public class MusicService extends Service {

    MediaPlayer mediaPlayer;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        try {

            mediaPlayer = new MediaPlayer();
            //sets the data source of audio file
            mediaPlayer.setDataSource(ListOfSongsActivity.absolutePath);
            //prepares the player for playback synchronously
            mediaPlayer.prepare();
            //sets the player for looping
            mediaPlayer.setLooping(true);
            //starts or resumes the playback
            mediaPlayer.start();

        } catch (IOException e) {
            e.printStackTrace();
            Log.i("show","Error: "+e.toString());
        }

        return START_STICKY;
    }

    public void onDestroy(){
        //stops the playback
        mediaPlayer.stop();
        //releases any resource attached with MediaPlayer object
        mediaPlayer.release();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

4. Permission checks for Android Marshmallow and higher versions

The latest Android versions(Android Marshmallow and higher) allows the user to install applications without granting permissions. So, the user needs to be asked to grant the requested permissions. Java has ContextCompat.checkSelfPermission which determines whether you have granted a particular permission or not. We have another method onRequestPermissionResult which is a callback for the result of requesting permissions. The code below is within ‘ListOfSongsActivity.java’.

//Handling permissions for Android Marshmallow and above
    void checkPermission(){
            if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
                //if permission granted, initialize the views
                initViews();
            }else{
                //show the dialog requesting to grant permission
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                }
        }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        switch (requestCode){
            case 1:
                if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    initViews();
                }else{
                    //permission is denied (this is the first time, when "never ask again" is not checked)
                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)){
                        finish();
                    }
                    //permission is denied (and never ask again is  checked)
                    else {
                        //shows the dialog describing the importance of permission, so that user should grant
                        AlertDialog.Builder builder = new AlertDialog.Builder(this);
                        builder.setMessage("You have forcefully denied Read storage permission.\n\nThis is necessary for the working of app."+"\n\n"+"Click on 'Grant' to grant permission")
                                //This will open app information where user can manually grant requested permission
                                .setPositiveButton("Grant", new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int id) {
                                        finish();
                                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                                Uri.fromParts("package", getPackageName(), null));
                                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                        startActivity(intent);
                                    }
                                })
                                //close the app
                                .setNegativeButton("Don't", new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int id) {
                                        finish();
                                    }
                                });
                        builder.setCancelable(false);
                        builder.create().show();
                    }
                }
        }
    }

Conclusion-

In this example, we saw how to use services to play background music. Not only that, we also learned how to fetch files from phone storage and how to implement permission checks for Android Marshmallow and higher versions. Hope you understand. In case of any queries, you may ask questions. Keep following more amazing Android Blogs.


If you really liked the article, please subscribe to our YouTube Channel for videos related to this article.Please find us on Twitter and Facebook.

Related Posts