Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Phát nhạc nền (Background Music) trong ứng dụng Android

Giới thiệu

Bài viết này hướng dẫn cách phát nhạc nền (background music) trong ứng dụng Android bằng cách sử dụng Service và MediaPlayer. Nhạc sẽ tiếp tục phát ngay cả khi người dùng thoát khỏi Activity (nhấn Home, mở app khác) - phù hợp cho game, app nghe nhạc, meditation.

Các bước thực hiện

Bước 1: Tạo dự án mới

Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.

Bước 2: Layout chính (res/layout/activity_main.xml)

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

    <Button
        android:id="@+id/btnPlay"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Phát nhạc nền" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Dừng nhạc" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Nhạc đang phát trong background"
        android:textSize="16sp"
        android:textStyle="bold" />

</LinearLayout>

Bước 3: Tạo file nhạc

Tạo thư mục res/raw/ (chuột phải res → New → Android Resource Directory → Type: raw) và copy file nhạc (ví dụ: background_music.mp3) vào đó.

Bước 4: Tạo BackgroundSoundService (BackgroundSoundService.java)

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.widget.Toast;
import androidx.annotation.Nullable;

public class BackgroundSoundService extends Service {

    private MediaPlayer mediaPlayer;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null; // Không hỗ trợ binding
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // Tạo MediaPlayer từ raw resource
        mediaPlayer = MediaPlayer.create(this, R.raw.background_music);
        if (mediaPlayer != null) {
            mediaPlayer.setLooping(true);        // Lặp lại liên tục
            mediaPlayer.setVolume(1.0f, 1.0f);   // Âm lượng max (0.0f - 1.0f)
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
            mediaPlayer.start();
            Toast.makeText(this, "Đang phát nhạc nền...", Toast.LENGTH_SHORT).show();
        }
        // START_STICKY: Hệ thống sẽ restart service nếu bị kill do thiếu bộ nhớ
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }
}

Bước 5: Activity chính (MainActivity.java)

import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button btnPlay, btnStop;

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

        btnPlay = findViewById(R.id.btnPlay);
        btnStop = findViewById(R.id.btnStop);

        btnPlay.setOnClickListener(v -> startBackgroundMusic());
        btnStop.setOnClickListener(v -> stopBackgroundMusic());
    }

    private void startBackgroundMusic() {
        Intent intent = new Intent(this, BackgroundSoundService.class);
        startService(intent);
    }

    private void stopBackgroundMusic() {
        Intent intent = new Intent(this, BackgroundSoundService.class);
        stopService(intent);
    }
}

Bước 6: Khai báo Service trong Manifest (androidManifest.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">

    <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/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                
                
            </intent-filter>
        </activity>

        <!-- KHAI BÁO SERVICE -->
        <service 
            android:name=".BackgroundSoundService"
            android:exported="false"
            android:foregroundServiceType="mediaPlayback" />

    </application>
</manifest>

Chạy ứng dụng

Kết nối thiết bị, nhấn Run. Nhấn "Phát nhạc nền" -> nhạc phát. Nhấn Home, mở app khác -> nhạc vẫn phát. Quay lại app, nhấn "Dừng nhạc" -> nhạc dừng.

Quan trọng: Foreground Service (Android 8.0+ / API 26+)

Từ Android 8.0, Service chạy background bị hạn chế. Phát nhạc nền bắt buộc dùng Foreground Service với Notification để tránh bị hệ thống kill.

Cập nhật BackgroundSoundService cho Foreground Service

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

public class BackgroundSoundService extends Service {

    private static final String CHANNEL_ID = "MusicPlaybackChannel";
    private static final int NOTIFICATION_ID = 101;
    private MediaPlayer mediaPlayer;

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
        mediaPlayer = MediaPlayer.create(this, R.raw.background_music);
        if (mediaPlayer != null) {
            mediaPlayer.setLooping(true);
            mediaPlayer.setVolume(1.0f, 1.0f);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
            mediaPlayer.start();
        }
        
        // Tạo notification cho foreground service
        Notification notification = createNotification();
        
        // Start foreground (API 26+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, notification, 
                ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
        } else {
            startForeground(NOTIFICATION_ID, notification);
        }
        
        return START_STICKY;
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                CHANNEL_ID, "Music Playback", NotificationManager.IMPORTANCE_LOW
            );
            channel.setDescription("Điều khiển phát nhạc nền");
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }

    private Notification createNotification() {
        Intent stopIntent = new Intent(this, BackgroundSoundService.class);
        stopIntent.setAction("STOP");
        PendingIntent stopPendingIntent = PendingIntent.getService(
            this, 0, stopIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT
        );

        return new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Đang phát nhạc")
            .setContentText("Nhấn để dừng")
            .setSmallIcon(R.drawable.ic_music_note)
            .addAction(R.drawable.ic_stop, "Dừng", stopPendingIntent)
            .setOngoing(true)
            .setCategory(NotificationCompat.CATEGORY_TRANSPORT)
            .build();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        stopForeground(true);
    }
}

Quyền cần thiết (Android 9+ / API 28+)

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> <!-- API 29+ -->

Thư viện hiện đại: Media3 / ExoPlayer

Đối với ứng dụng phát nhạc chuyên nghiệp, nên dùng Media3 (ExoPlayer) với MediaSessionService - hỗ trợ lock screen controls, Bluetooth, Assistant, Cast, v.v.

implementation "androidx.media3:media3-session:1.4.0"
implementation "androidx.media3:media3-exoplayer:1.4.0"

// MediaSessionService tự xử lý foreground, notification, media controls

Lưu ý quan trọng

  • Luôn dùng Foreground Service cho phát nhạc nền từ API 26+.
  • android:exported="false" cho service chỉ dùng nội bộ app.
  • FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK (API 29+) bắt buộc khai báo.
  • Xử lý audio focus: AudioManager.requestAudioFocus() để tạm dừng khi có cuộc gọi, alarm...
  • MediaPlayer cần release() kịp thời tránh leak native memory.
  • Test trên Android 14+ (foreground service behavior thay đổi).