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:
- Panggil
prepareAsync()
, bukanprepare()
, dan - Terapkan
MediaPlayer.OnPreparedListener
agar diberi tahu saat persiapan selesai dan Anda dapat mulai bermain.
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: