Operations that are time-consuming and suitable for background execution, such as music playback, should be performed in a Service rather than an Activity. Below, we’ll introduce a music player implementation using a Service.
Step 1: Create a MusicService¶
In Android Studio, right-click on the package name, select New → Service → Service.

When created this way, the Service declaration is automatically added to the manifest file.

Step 2: Implement MusicService¶
The MusicService handles music playback logic, overriding onCreate() for player initialization.
public class MusicService extends Service {
private String path = "mnt/sdcard/123.mp3"; // Local audio file path
private MediaPlayer player;
@Override
public IBinder onBind(Intent intent) {
return new MyBinder(); // Return control methods to MainActivity
}
@Override
public void onCreate() {
super.onCreate();
player = new MediaPlayer();
try {
player.setDataSource(path);
player.prepare(); // Synchronous preparation for local files
} catch (IOException e) {
e.printStackTrace();
}
Log.e("Service", "Music prepared for playback");
}
// Inner class to expose music control methods
public class MyBinder extends Binder {
public boolean isPlaying() {
return player.isPlaying();
}
public void play() {
if (player.isPlaying()) {
player.pause();
} else {
player.start();
}
Log.e("Service", "Music playback control triggered");
}
public int getDuration() {
return player.getDuration(); // Duration in milliseconds
}
public int getCurrentPosition() {
return player.getCurrentPosition(); // Current progress in milliseconds
}
public void seekTo(int ms) {
player.seekTo(ms); // Seek to specified position
}
}
}
Step 3: Design the Layout¶
Add a button and a progress bar to control playback.
<Button
android:id="@+id/play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="play"
android:text="Play" />
<SeekBar
android:id="@+id/sb"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Step 4: Implement MainActivity¶
The Activity binds to MusicService to control playback and update the UI.
public class MainActivity extends AppCompatActivity {
private MusicService.MyBinder musicControl;
private Button playBtn;
private SeekBar seekBar;
private static final int UPDATE_PROGRESS = 0;
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == UPDATE_PROGRESS) {
updateProgress();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
playBtn = findViewById(R.id.play);
seekBar = findViewById(R.id.sb);
// Start and bind to the service
Intent intent = new Intent(this, MusicService.class);
startService(intent);
bindService(intent, new MyConnection(), BIND_AUTO_CREATE);
// SeekBar listener
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
musicControl.seekTo(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
}
// Service connection to get MyBinder instance
private class MyConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
musicControl = (MusicService.MyBinder) service;
updatePlayText();
seekBar.setMax(musicControl.getDuration());
seekBar.setProgress(musicControl.getCurrentPosition());
}
@Override
public void onServiceDisconnected(ComponentName name) {}
}
@Override
protected void onResume() {
super.onResume();
if (musicControl != null) {
handler.sendEmptyMessage(UPDATE_PROGRESS);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(new MyConnection());
}
@Override
protected void onStop() {
super.onStop();
handler.removeCallbacksAndMessages(null); // Stop progress updates
}
// Update progress bar periodically
private void updateProgress() {
int current = musicControl.getCurrentPosition();
seekBar.setProgress(current);
handler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 500); // Update every 500ms
}
// Toggle play/pause and update button text
public void play(View view) {
musicControl.play();
updatePlayText();
}
private void updatePlayText() {
if (musicControl.isPlaying()) {
playBtn.setText("Pause");
handler.sendEmptyMessage(UPDATE_PROGRESS);
} else {
playBtn.setText("Play");
}
}
}
Step 5: Add Permissions¶
Include storage access permission for local files:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Effect¶

Bonus: Network Music Playback¶
To play music from a URL, use asynchronous preparation and a listener:
private MediaPlayer player;
public void playNetworkMusic() {
if (player == null) {
player = new MediaPlayer();
try {
player.setDataSource("http://www.w3school.com.cn/i/horse.mp3");
player.prepareAsync(); // Asynchronous preparation for network files
player.setOnPreparedListener(mp -> mp.start()); // Start after preparation
} catch (IOException e) {
e.printStackTrace();
}
} else {
if (player.isPlaying()) {
player.pause();
} else {
player.start();
}
}
}
Add network permission:
<uses-permission android:name="android.permission.INTERNET" />
This implementation separates UI (Activity) from background logic (Service), ensuring smooth music playback without blocking the UI thread.