Play music using MediaPlayer in Service

This post describes an example of a music player which plays the audio file picked. The MediaPlayer runs in Service class which continues to run even when app is closed. The MediaPlayer is controlled by the Activity bound to the Service, which unbinds when the Activity exits and binds again when the Activity starts.

Create your layout in activity_main.xml.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="PICK FILE"
        app:layout_constraintBottom_toTopOf="@+id/linear1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/linear1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp"
        android:background="#ffffdd"
        android:gravity="center_horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button1">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textView2"
            android:textSize="18dp"
            android:text="Title"/>
        <SeekBar
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/seekbar1"
            android:max="100"
            android:padding="10dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textView3"
            android:textSize="20dp"
            android:padding="10dp"
            android:text="00:00 / 00:00"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="PLAY"
            android:id="@+id/button2"/>
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

Create a java file, PlayerService.java and add following code.

package com.example.pickfile;

import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.media.AudioAttributes;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.provider.MediaStore;

import androidx.annotation.Nullable;

import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class PlayerService extends Service {

    private final IBinder binder = new LocalBinder();
    MediaPlayer mediaPlayer;
    String duration;
    ScheduledExecutorService timer;
    SharedPreferences sp;
    Uri mUri;
    String filename;

    public class LocalBinder extends Binder {
        PlayerService getService() {
            // Return this instance of LocalService so clients can call public methods
            return PlayerService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mUri = intent.getData();
        createMediaPlayer(mUri);
        sp = getSharedPreferences("sp", MODE_PRIVATE);
        return START_STICKY;
    }

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

    public void createMediaPlayer(Uri uri){
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioAttributes(
                new AudioAttributes.Builder()
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .build()
        );
        try {
            mediaPlayer.setDataSource(getApplicationContext(), uri);
            mediaPlayer.prepare();
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    releaseMediaPlayer();
                    stopSelf();
                }
            });
            filename = getNameFromUri(uri);
            int millis = mediaPlayer.getDuration();
            long total_secs = TimeUnit.SECONDS.convert(millis, TimeUnit.MILLISECONDS);
            long mins = TimeUnit.MINUTES.convert(total_secs, TimeUnit.SECONDS);
            long secs = total_secs - (mins*60);
            duration = mins + ":" + secs;
        } catch (IOException e){
            MainActivity.textview2.setText(e.toString());
        }
    }

    public void updateMediaData(){
        MainActivity.textview2.setText(filename);
        MainActivity.button2.setEnabled(true);
        int millis = mediaPlayer.getDuration();
        MainActivity.textview3.setText("00:00 / " + duration);
        MainActivity.seekbar1.setMax(millis);
        MainActivity.seekbar1.setProgress(0);
    }

    public String getNameFromUri(Uri uri){
        String fileName = "";
        Cursor cursor = null;
        cursor = getContentResolver().query(uri, new String[]{
                MediaStore.Images.ImageColumns.DISPLAY_NAME
        }, null, null, null);

        if (cursor != null && cursor.moveToFirst()) {
            fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DISPLAY_NAME));
        }
        if (cursor != null) {
            cursor.close();
        }
        return fileName;
    }

    public void releaseMediaPlayer(){
        if (timer != null) {
            timer.shutdown();
        }
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }
        sp.edit().putString("created", "false").commit();
        MainActivity.button2.setEnabled(false);
        MainActivity.textview2.setText("TITLE");
        MainActivity.textview3.setText("00:00 / 00:00");
        MainActivity.seekbar1.setMax(100);
        MainActivity.seekbar1.setProgress(0);
    }

    public void playOrPause() {
        if (mediaPlayer != null) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.pause();
                MainActivity.button2.setText("PLAY");
                timer.shutdown();
            } else {
                mediaPlayer.start();
                MainActivity.button2.setText("PAUSE");

                timer = Executors.newScheduledThreadPool(1);
                timer.scheduleAtFixedRate(new Runnable() {
                    @Override
                    public void run() {
                        if (mediaPlayer != null) {
                            if (!MainActivity.seekbar1.isPressed()) {
                                MainActivity.seekbar1.setProgress(mediaPlayer.getCurrentPosition());
                            }
                        }
                    }
                }, 10, 10, TimeUnit.MILLISECONDS);
            }
        }
    }

    public void updatePlayingTime(){
        if (mediaPlayer != null){
            int millis = mediaPlayer.getCurrentPosition();
            long total_secs = TimeUnit.SECONDS.convert(millis, TimeUnit.MILLISECONDS);
            long mins = TimeUnit.MINUTES.convert(total_secs, TimeUnit.SECONDS);
            long secs = total_secs - (mins*60);
            MainActivity.textview3.setText(mins + ":" + secs + " / " + duration);
        }
    }

    public void seekMediaPlayer(){
        if (mediaPlayer != null) {
            mediaPlayer.seekTo(MainActivity.seekbar1.getProgress());
        }
    }

}

In AndroidManifest.xml add the Service class.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.pickfile">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.PickFile">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".PlayerService"/>
    </application>

</manifest>

In MainActivity.java put following codes.

package com.example.pickfile;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    static TextView textview2;
    static TextView textview3;
    Button button1;
    static  Button button2;
    static SeekBar seekbar1;

    boolean mBound;
    PlayerService mService;
    public static final int PICK_FILE =99;
    SharedPreferences sp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);
        textview2 = findViewById(R.id.textView2);
        textview3 = findViewById(R.id.textView3);
        seekbar1 = findViewById(R.id.seekbar1);

        sp = getSharedPreferences("sp", MODE_PRIVATE);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("audio/*");
                startActivityForResult(intent, PICK_FILE);
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mBound){
                    mService.playOrPause();
                }
            }
        });

        seekbar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (mBound) {
                    mService.updatePlayingTime();
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                if (mBound) {
                    mService.seekMediaPlayer();
                }
            }
        });
        button2.setEnabled(false);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PICK_FILE && resultCode == RESULT_OK){
            if (data != null){
                Uri uri = data.getData();
                Intent intent = new Intent(MainActivity.this, PlayerService.class);
                intent.setData(uri);
                startService(intent);
                bindService(intent, connection, Context.BIND_AUTO_CREATE);
                sp.edit().putString("created", "true").commit();
            }
        }
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            PlayerService.LocalBinder binder = (PlayerService.LocalBinder) service;
            mService = binder.getService();
            if (mService != null){
                mService.updateMediaData();
            }
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };

    @Override
    protected void onStop() {
        super.onStop();
        if (mService != null){
            if (mBound){
                unbindService(connection);
                mBound=false;
            }
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
            if (!mBound){
                if (sp.getString("created", "").equals("true")) {
                        Intent intent = new Intent(MainActivity.this, PlayerService.class);
                        bindService(intent, connection, Context.BIND_AUTO_CREATE);
                }
            }
    }
  
}

Now run the app.

Leave a Reply