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:
- Memanggil
prepareAsync()dan bukanprepare() - Mengimplementasikan
MediaPlayer.OnPreparedListeneragar diberi tahu saat persiapan selesai dan Anda dapat mulai melakukan pemutaran.
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: