Memutar media di latar belakang

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

Untuk melakukannya, Anda menyematkan MediaPlayer dalam MediaBrowserServiceCompat layanan 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 seluruh bagian 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 jika Anda menerapkannya dalam layanan.

Menjalankan secara asinkron

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

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

Misalnya, saat Anda 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 tersinkron, 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 mengimplementasikan MediaPlayer.OnErrorListener dan menyetelnya 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 terjadi error, MediaPlayer akan beralih ke status Error. Anda harus menyetelnya kembali 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 penguncian layar saat aktif untuk mencegah sistem mengganggu pemutaran, misalnya, dengan membuat perangkat masuk ke mode tidur.

Penguncian layar saat aktif merupakan 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 sedang diputar, panggil metode setWakeMode() saat Anda menginisialisasi MediaPlayer. MediaPlayer akan mempertahankan penguncian yang ditentukan sembari melakukan pemutaran, lalu membukanya 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 menjamin bahwa CPU akan tetap aktif. Jika Anda melakukan streaming media melalui jaringan dan menggunakan Wi-Fi, Anda mungkin juga perlu menambahkan WifiLock, yang harus diperoleh dan dirilis secara manual. Jadi, saat mulai mempersiapkan the 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 dijelaskan, 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 bagi Anda untuk memanggil metode pembersihan ini secara eksplisit daripada mengandalkan pembersihan sampah memori karena fitur ini memerlukan beberapa saat sebelum mengklaim ulang MediaPlayer, karena fitur ini hanya sensitif terhadap kebutuhan memori tanpa memperhatikan kekurangan resource terkait media lainnya. Jadi, jika menggunakan layanan, Anda harus selalu mengganti metode onDestroy() untuk memastikan bahwa akan selalu dirilis 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 di samping merilisnya saat ditutup. Sebagai contoh, jika Anda ingin media berhenti diputar setelah jangka waktu yang terlalu lama (misalnya setelah kehilangan fokus audio), Anda tentu harus merilis yang sudah ada MediaPlayer dan membuatnya lagi nanti. Di sisi lain, jika Anda hanya ingin pemutaran dihentikan 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.

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