Możesz odtwarzać multimedia w tle, nawet gdy aplikacja nie jest widoczna na ekranie, np. gdy użytkownik korzysta z innych aplikacji.
Aby to zrobić, umieść MediaPlayer w usłudze MediaBrowserServiceCompat i spraw, aby wchodził w interakcję z MediaBrowserCompat w innej aktywności.
Zachowaj ostrożność podczas wdrażania tej konfiguracji klienta i serwera. Istnieją oczekiwania dotyczące tego, jak odtwarzacz działający w usłudze w tle wchodzi w interakcję z pozostałą częścią systemu. Jeśli aplikacja nie spełnia tych oczekiwań, użytkownik może być niezadowolony. Szczegółowe informacje znajdziesz w artykule Tworzenie aplikacji audio.
Na tej stronie znajdziesz specjalne instrukcje dotyczące zarządzania obiektem MediaPlayer, gdy zaimplementujesz go w usłudze.
Uruchom asynchronicznie
Podobnie jak w przypadku Activity, cała praca w Service jest domyślnie wykonywana w jednym wątku. Gdy uruchamiasz działanie i usługę z tej samej aplikacji, domyślnie korzystają one z tego samego wątku („wątku głównego”).
Usługi muszą szybko przetwarzać przychodzące intencje i nigdy nie wykonywać długotrwałych obliczeń w odpowiedzi na nie. Wszelkie wymagające operacje i wywołania blokujące musisz wykonywać asynchronicznie: w innym wątku, który sam zaimplementujesz, lub za pomocą wielu funkcji platformy do przetwarzania asynchronicznego.
Jeśli na przykład używasz funkcji MediaPlayer w głównym wątku, powinnaś(-eś):
- dzwonić pod numer
prepareAsync()zamiastprepare(); - Zaimplementuj
MediaPlayer.OnPreparedListener, aby otrzymywać powiadomienia, gdy przygotowanie się zakończy i będziesz mieć możliwość rozpoczęcia gry.
Przykład:
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();
}
}
Obsługa błędów asynchronicznych
W przypadku operacji synchronicznych błędy są sygnalizowane za pomocą wyjątku lub kodu błędu. Jeśli jednak używasz zasobów asynchronicznych, musisz odpowiednio powiadamiać aplikację o błędach. W przypadku MediaPlayer implementujesz MediaPlayer.OnErrorListener i ustawiasz go w instancji 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!
}
}
Gdy wystąpi błąd, MediaPlayer przechodzi w stan Błąd. Musisz go zresetować, zanim będziesz mógł go ponownie użyć. Szczegółowe informacje znajdziesz na pełnym diagramie stanu klasy MediaPlayer.
Korzystanie z blokad uśpienia
Podczas odtwarzania lub przesyłania strumieniowego muzyki w tle musisz używać blokad uśpienia, aby zapobiec ingerencji systemu w odtwarzanie, np. przez uśpienie urządzenia.
Blokada uśpienia to sygnał dla systemu, że aplikacja korzysta z funkcji, które powinny być dostępne nawet wtedy, gdy telefon jest nieaktywny.
Aby mieć pewność, że procesor będzie działać podczas odtwarzania MediaPlayer, wywołaj metodę setWakeMode() podczas inicjowania MediaPlayer. MediaPlayer utrzymuje określoną blokadę podczas odtwarzania i zwalnia ją po wstrzymaniu lub zatrzymaniu:
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);
Blokada uśpienia uzyskana w tym przykładzie zapewnia jednak tylko to, że procesor pozostanie aktywny. Jeśli przesyłasz strumieniowo multimedia przez sieć i korzystasz z Wi-Fi, prawdopodobnie chcesz też mieć WifiLock, który musisz uzyskać i zwolnić ręcznie. Gdy zaczniesz przygotowywać MediaPlayer za pomocą zdalnego adresu URL, utwórz i uzyskaj blokadę Wi-Fi.
Przykład:
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();
Gdy wstrzymasz lub zatrzymasz odtwarzanie multimediów albo gdy sieć nie będzie już potrzebna, zwolnij blokadę:
Kotlin
wifiLock.release()
Java
wifiLock.release();
Przeprowadź czyszczenie
Jak wspomnieliśmy wcześniej, obiekt MediaPlayer może zużywać znaczną ilość zasobów systemowych, dlatego należy go przechowywać tylko tak długo, jak jest potrzebny, a po zakończeniu pracy z nim wywołać metodę release(). Ważne jest, aby wywoływać tę metodę czyszczenia jawnie, a nie polegać na czyszczeniu pamięci przez system, ponieważ może to zająć trochę czasu, zanim czyszczenie pamięci odzyska MediaPlayer. Jest ono wrażliwe tylko na potrzeby pamięci, a nie na niedobór innych zasobów związanych z multimediami. Jeśli więc korzystasz z usługi, zawsze zastępuj metodę onDestroy(), aby mieć pewność, że zwalniasz 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();
}
}
Zawsze szukaj innych możliwości uwolnienia MediaPlayer, oprócz zwalniania go podczas wyłączania. Jeśli na przykład przewidujesz, że przez dłuższy czas nie będziesz mieć możliwości odtwarzania multimediów (np. po utracie aktywności audio), zdecydowanie powinieneś zwolnić istniejący MediaPlayer i utworzyć go ponownie później. Z drugiej strony, jeśli planujesz zatrzymać odtwarzanie tylko na bardzo krótki czas, prawdopodobnie lepiej będzie zachować MediaPlayer, aby uniknąć ponownego tworzenia i przygotowywania.
Więcej informacji
Jetpack Media3 to zalecane rozwiązanie do odtwarzania multimediów w aplikacji. Więcej informacji
Na tych stronach znajdziesz informacje o nagrywaniu, przechowywaniu i odtwarzaniu dźwięku i obrazu: