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

Cách sử dụng Android MediaPlayer Singleton

MediaPlayer Singleton - Quản lý phát nhạc tập trung

MediaPlayer là tài nguyên nặng (heavy resource). Singleton pattern giúp tái sử dụng một instance, tránh tạo nhiều MediaPlayer gây rò rỉ bộ nhớ hoặc lỗi "MediaPlayer went away with unhandled events".

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

Android Studio → File → New Project. Thêm file âm thanh sample.mp3 vào res/raw/.

Bước 2: Giao diện (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:gravity="center">

    <Button
        android:id="@+id/play"
        android:text="Phát nhạc từ Singleton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

Bước 3: MainActivity.java

package com.example.mediaplayersingleton;

import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    Button btnPlay;
    MediaPlayerSingleton mediaPlayerSingleton;

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

        btnPlay = findViewById(R.id.play);
        mediaPlayerSingleton = MediaPlayerSingleton.getInstance();
        mediaPlayerSingleton.init(getApplicationContext());

        btnPlay.setOnClickListener(v -> {
            MediaPlayer player = mediaPlayerSingleton.getMediaPlayer();
            if (player != null) {
                player.start();
            } else {
                Toast.makeText(this, "MediaPlayer chưa sẵn sàng", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mediaPlayerSingleton.release();
    }
}

Bước 4: MediaPlayerSingleton.java

package com.example.mediaplayersingleton;

import android.content.Context;
import android.media.MediaPlayer;

public class MediaPlayerSingleton {
    private static MediaPlayerSingleton instance;
    private MediaPlayer mediaPlayer;
    private Context appContext;

    private MediaPlayerSingleton() { }

    public static synchronized MediaPlayerSingleton getInstance() {
        if (instance == null) {
            instance = new MediaPlayerSingleton();
        }
        return instance;
    }

    public void init(Context context) {
        if (appContext == null) {
            this.appContext = context.getApplicationContext();
        }
    }

    public MediaPlayer getMediaPlayer() {
        if (mediaPlayer == null) {
            mediaPlayer = MediaPlayer.create(appContext, R.raw.sample);
            mediaPlayer.setOnCompletionListener(mp -> {
                mp.seekTo(0); // Tự động quay về đầu khi hết bài
            });
        }
        return mediaPlayer;
    }

    public void pause() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        }
    }

    public void stop() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.reset();
            mediaPlayer = null; // Cho phép tạo lại lần sau
        }
    }

    public void release() {
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    public boolean isPlaying() {
        return mediaPlayer != null && mediaPlayer.isPlaying();
    }
}

Chạy ứng dụng

Nhấn nút → phát file sample.mp3 từ raw resource. Nhấn lại khi đang phát → tiếp tục từ vị trí hiện tại (do không reset).

Các phương thức quan trọng

  • getMediaPlayer(): Lazy initialization - chỉ tạo khi cần.
  • release(): **Bắt buộc gọi trong onDestroy()** để giải phóng native resources.
  • stop() + reset(): Dừng và chuẩn bị cho lần phát sau.
  • setOnCompletionListener: Xử lý khi bài hát kết thúc.

Cải tiến cho Production

// Thêm MediaPlayer.OnErrorListener
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
    Log.e("MediaPlayer", "Error: " + what);
    release();
    return true; // Đã xử lý lỗi
});

// Thêm OnPreparedListener cho streaming
mediaPlayer.setOnPreparedListener(mp -> mp.start());

// Xử lý Audio Focus
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.requestAudioFocus(focusChange -> {}, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

⚠️ Lưu ý quan trọng

  1. Luôn gọi release() khi không dùng nữa (onDestroy, onStop).
  2. Không giữ MediaPlayer static mà không release - gây leak native memory.
  3. Xử lý lifecycle: pause trong onPause, resume trong onResume.
  4. Dùng MediaPlayer.create() thay vì new MediaPlayer() + setDataSource() + prepare() cho file raw/local.