odtwarzać multimedia w tle,

Możesz odtwarzać multimedia w tle, nawet gdy aplikacja nie jest widoczna na ekranie, np. gdy użytkownik korzysta z innych aplikacji.

W tym celu należy umieścić MediaPlayer w usłudze MediaBrowserServiceCompat i skonfigurować jego interakcję z elementem MediaBrowserCompat w innej aktywności.

Podczas wdrażania tej konfiguracji klienta i serwera należy zachować ostrożność. Istnieją oczekiwania dotyczące tego, jak odtwarzacz działający w tle współpracuje z resztą systemu. Jeśli aplikacja nie spełnia tych oczekiwań, użytkownik może być niezadowolony. Więcej informacji znajdziesz w artykule Tworzenie aplikacji audio.

Na tej stronie znajdziesz specjalne instrukcje zarządzania MediaPlayerem, gdy jest on używany w usłudze.

Wykonywanie asynchroniczne

Podobnie jak w przypadku Activity, wszystkie działania w Service są domyślnie wykonywane w ramach jednego wątku. W zasadzie, gdy uruchamiasz aktywność i usługę z tej samej aplikacji, domyślnie korzystają one z tego samego wątku („głównego wątku”).

Usługi muszą szybko przetwarzać przychodzące intencje i nigdy nie wykonywać długich obliczeń podczas odpowiadania na nie. Wszystkie wymagające dużej mocy obliczeniowej operacje i blokujące wywołania należy wykonywać asynchronicznie: albo z innego wątku, który implementujesz samodzielnie, albo za pomocą wielu funkcji frameworka do przetwarzania asynchronicznego.

Jeśli na przykład używasz MediaPlayer w głównym wątku, musisz:

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 poinformować aplikację o błędach. W przypadku MediaPlayer zaimplementuj MediaPlayer.OnErrorListener i skonfiguruj 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ędzie można go ponownie użyć. Szczegółowe informacje znajdziesz na diagramie stanów klasy MediaPlayer.

Używanie blokad uśpienia

Podczas odtwarzania lub przesyłania strumieniowego muzyki w tle musisz używać blokad uśpienia, aby system nie zakłócał odtwarzania, na przykład przez przełączenie urządzenia w tryb uśpienia.

Blokada wybudzania to sygnał dla systemu, że aplikacja korzysta z funkcji, które powinny być dostępne nawet wtedy, gdy telefon jest nieaktywny.

Aby zapewnić, że procesor będzie nadal działać, gdy MediaPlayer jest odtwarzany, wywołaj metodę setWakeMode() podczas inicjowania MediaPlayer. MediaPlayer zachowuje określony stan blokady podczas odtwarzania i odblokowuje go 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 pozostaje aktywny. Jeśli przesyłasz strumieniowo multimedia przez sieć i używasz Wi-Fi, prawdopodobnie chcesz też zablokować WifiLock, który musisz uzyskać i zwolnić ręcznie. Gdy więc zaczniesz przygotowywać MediaPlayer z użyciem adresu URL zdalnego, musisz utworzyć i uzyskać 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 multimediów lub gdy nie będziesz już potrzebować sieci, odblokuj:

Kotlin

wifiLock.release()

Java

wifiLock.release();

Czyszczenie

Jak już wspomnieliśmy, obiekt MediaPlayer może zużywać znaczną ilość zasobów systemowych, dlatego należy go przechowywać tylko tak długo, jak jest to konieczne, a po zakończeniu pracy wywołać metodę release(). Należy wywołać tę metodę czyszczenia wprost, a nie polegać na systemie zbierania elementów do usunięcia, ponieważ może minąć trochę czasu, zanim zbieracz elementów do usunięcia odzyskaMediaPlayer, ponieważ jest on wrażliwy tylko na potrzeby pamięci, a nie na niedobór innych zasobów związanych z multimediami. Jeśli 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();
   }
}

Oprócz zwalniania MediaPlayer podczas wyłączania urządzenia, zawsze szukaj innych możliwości zwolnienia. Jeśli na przykład przewidujesz, że nie będziesz mieć możliwości odtwarzania multimediów przez dłuższy czas (np. po utracie skupienia na dźwięku), zdecydowanie zwolnij bieżący MediaPlayer i utwórz go ponownie później. Jeśli jednak zamierzasz zatrzymać odtwarzanie tylko na bardzo krótki czas, prawdopodobnie warto poczekać z MediaPlayer, aby uniknąć konieczności 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 na temat nagrywania, przechowywania i odtwarzania dźwięku i obrazu: