Memutar media di latar belakang

Anda dapat memutar media di latar belakang meskipun aplikasi tidak ditampilkan di layar, misalnya, saat pengguna berinteraksi dengan aplikasi lain.

Untuk melakukannya, Anda menyematkan MediaPlayer dalam layanan MediaBrowserServiceCompat dan membuatnya berinteraksi dengan MediaBrowserCompat dalam aktivitas lain.

Berhati-hatilah saat menerapkan penyiapan klien dan server ini. Ada ekspektasi tentang cara pemutar yang berjalan di layanan latar belakang berinteraksi dengan sistem lainnya. Jika aplikasi Anda tidak memenuhi ekspektasi tersebut, pengguna mungkin akan mendapatkan pengalaman yang buruk. Lihat Mem-build Aplikasi Audio untuk mengetahui detailnya.

Halaman ini menjelaskan petunjuk khusus untuk mengelola MediaPlayer saat Anda menerapkan MediaPlayer di dalam layanan.

Berjalan secara asinkron

Seperti Activity, semua tugas dalam Service dilakukan dalam satu thread secara default. Faktanya, saat Anda menjalankan aktivitas dan layanan dari aplikasi yang sama, keduanya akan menggunakan thread yang sama ("thread utama") secara default.

Layanan harus memproses intent yang masuk dengan cepat dan tidak pernah melakukan komputasi yang panjang saat meresponsnya. Anda harus melakukan tugas berat atau panggilan pemblokir secara asinkron: baik dari thread lain yang Anda terapkan sendiri, maupun menggunakan banyak fasilitas framework untuk pemrosesan asinkron.

Misalnya, saat menggunakan MediaPlayer dari thread utama, Anda harus:

Contoh:

Kotlin

private const val ACTION_PLAY: String = "com.example.action.PLAY"

class MyService: Service(), MediaPlayer.OnPreparedListener {

    private var mMediaPlayer: MediaPlayer? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        ...
        val action: String = intent.action
        when(action) {
            ACTION_PLAY -> {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer?.apply {
                    setOnPreparedListener(this@MyService)
                    prepareAsync() // prepare async to not block main thread
                }

            }
        }
        ...
    }

    /** Called when MediaPlayer is ready */
    override fun onPrepared(mediaPlayer: MediaPlayer) {
        mediaPlayer.start()
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final String ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mediaPlayer = ... // initialize it here
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

Menangani error asinkron

Pada operasi sinkron, error ditandai dengan pengecualian atau kode error. Namun, saat menggunakan resource asinkron, Anda harus memberi tahu aplikasi tentang error dengan cara yang tepat. Untuk MediaPlayer, Anda menerapkan MediaPlayer.OnErrorListener dan menetapkannya dalam instance MediaPlayer:

Kotlin

class MyService : Service(), MediaPlayer.OnErrorListener {

    private var mediaPlayer: MediaPlayer? = null

    fun initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer?.setOnErrorListener(this)
    }

    override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Java

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Saat error terjadi, MediaPlayer akan beralih ke status Error. Anda harus meresetnya sebelum dapat menggunakannya lagi. Untuk mengetahui detailnya, lihat diagram status lengkap untuk class MediaPlayer.

Menggunakan penguncian layar saat aktif

Saat memutar atau melakukan streaming musik di latar belakang, Anda harus menggunakan wake lock untuk mencegah sistem mengganggu pemutaran, misalnya, dengan membuat perangkat masuk ke mode tidur.

Kunci layar saat aktif adalah sinyal ke sistem bahwa aplikasi Anda menggunakan fitur yang harus tetap tersedia meskipun ponsel sedang tidak ada aktivitas.

Untuk memastikan CPU terus berjalan saat MediaPlayer diputar, panggil metode setWakeMode() saat Anda melakukan inisialisasi MediaPlayer. MediaPlayer mempertahankan kunci yang ditentukan saat bermain dan melepaskan kunci saat dijeda atau dihentikan:

Kotlin

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}

Java

mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

Namun, penguncian layar saat aktif yang diperoleh dalam contoh ini hanya memastikan bahwa CPU tetap aktif. Jika Anda melakukan streaming media melalui jaringan dan menggunakan Wi-Fi, Anda mungkin juga perlu menambahkan WifiLock, yang harus Anda peroleh dan rilis secara manual. Jadi, saat mulai mempersiapkan MediaPlayer dengan URL jarak jauh, Anda harus membuat dan memperoleh penguncian Wi-Fi.

Contoh:

Kotlin

val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
    wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")

wifiLock.acquire()

Java

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

Saat menjeda atau menghentikan media, atau saat tidak lagi memerlukan jaringan, Anda harus merilis penguncian tersebut:

Kotlin

wifiLock.release()

Java

wifiLock.release();

Melakukan pembersihan

Seperti yang sudah disebutkan sebelumnya, objek MediaPlayer dapat menggunakan resource sistem dalam jumlah yang signifikan sehingga Anda hanya perlu mempertahankannya selama yang dibutuhkan dan memanggil release() setelah selesai menggunakannya. Penting untuk memanggil metode pembersihan ini secara eksplisit, bukan mengandalkan pembersihan sampah memori sistem karena mungkin perlu waktu beberapa saat sebelum pengumpulan sampah memori mengklaim ulang MediaPlayer, karena hanya sensitif terhadap kebutuhan memori dan bukan kekurangan resource terkait media lainnya. Jadi, jika menggunakan layanan, Anda harus selalu mengganti metode onDestroy() untuk memastikan bahwa Anda merilis MediaPlayer:

Kotlin

class MyService : Service() {

    private var mediaPlayer: MediaPlayer? = null
    // ...

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}

Java

public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

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

Anda juga harus selalu mencari kesempatan lain untuk merilis MediaPlayer, selain merilisnya saat dimatikan. Misalnya, jika Anda tidak dapat memutar media dalam jangka waktu yang lama (misalnya setelah kehilangan fokus audio), Anda harus merilis MediaPlayer yang ada dan membuatnya lagi nanti. Di sisi lain, jika Anda hanya ingin menghentikan pemutaran untuk waktu yang sangat singkat, sebaiknya pertahankan MediaPlayer untuk menghindari overhead proses pembuatan dan penyiapan ulang.

Pelajari lebih lanjut

Jetpack Media3 adalah solusi yang direkomendasikan untuk pemutaran media di aplikasi Anda. Baca selengkapnya tentang hal ini.

Halaman ini membahas topik yang berkaitan dengan perekaman, penyimpanan, dan pemutaran kembali audio dan video: