Zarządzaj aktywnością audio

Co najmniej 2 aplikacje na Androida mogą jednocześnie odtwarzać dźwięk w tym samym strumieniu wyjściowym, a system wszystko miksuje. Choć jest to imponujące pod względem technicznym, może być bardzo irytujące dla użytkownika. Aby uniknąć jednoczesnego odtwarzania muzyki przez wszystkie aplikacje, Android wprowadza koncepcję fokusowania dźwięku. Tylko jedna aplikacja może mieć fokus audio w danym momencie.

Gdy aplikacja musi odtwarzać dźwięk, powinna poprosić o skupienie dźwięku. Gdy ma fokus, może odtwarzać dźwięk. Jednak po uzyskaniu fokusu audio możesz nie być w stanie utrzymać go do końca odtwarzania. Inna aplikacja może poprosić o skupienie, co spowoduje przerwanie Twojego dostępu do skupienia dźwięku. W takim przypadku aplikacja powinna wstrzymać odtwarzanie lub zmniejszyć głośność, aby użytkownicy mogli łatwiej usłyszeć nowe źródło dźwięku.

W wersjach Androida starszych niż 12 (poziom interfejsu API 31) system nie zarządza fokusem dźwięku. Dlatego zachęcamy deweloperów aplikacji do przestrzegania wytycznych dotyczących fokusu audio, ale jeśli aplikacja nadal odtwarza dźwięk głośno nawet po utracie fokusu audio na urządzeniu z Androidem 11 (API na poziomie 30) lub starszym, system nie może temu zapobiec. Takie zachowanie aplikacji obniża komfort użytkowników i często prowadzi do odinstalowania przez nich aplikacji, która działa nieprawidłowo.

Dobrze zaprojektowana aplikacja audio powinna zarządzać fokusem dźwięku zgodnie z tymi ogólnymi wytycznymi:

  • Zadzwoń pod numer requestAudioFocus() bezpośrednio przed rozpoczęciem gry i sprawdź, czy połączenie zostało nawiązane AUDIOFOCUS_REQUEST_GRANTED. Wykonaj połączenie z requestAudioFocus()onPlay() wywołaniu zwrotnym sesji multimedialnej.

  • Gdy inna aplikacja uzyska fokus dźwięku, zatrzymaj lub wstrzymaj odtwarzanie albo zmniejsz głośność.

  • Gdy odtwarzanie się zatrzyma (np. gdy aplikacja nie ma już nic do odtworzenia), zrezygnuj z fokusowania dźwięku. Aplikacja nie musi rezygnować z fokusu audio, jeśli użytkownik wstrzyma odtwarzanie, ale może je później wznowić.

  • Użyj AudioAttributes, aby opisać rodzaj dźwięku odtwarzanego przez aplikację. Na przykład w przypadku aplikacji, które odtwarzają mowę, podaj wartość CONTENT_TYPE_SPEECH.

Fokus dźwięku jest obsługiwany w różny sposób w zależności od wersji Androida:

Android 12 (poziom 31 interfejsu API) lub nowszy
Fokus dźwięku jest zarządzany przez system. Gdy inna aplikacja poprosi o aktywność audio, system wymusi wyciszenie odtwarzania dźwięku z aplikacji. System wycisza też odtwarzanie dźwięku, gdy odbierasz połączenie przychodzące.
Android 8.0 (poziom 26 interfejsu API) do Androida 11 (poziom 30 interfejsu API)
Fokus dźwięku nie jest zarządzany przez system, ale obejmuje pewne zmiany, które zostały wprowadzone w Androidzie 8.0 (interfejs API na poziomie 26).
Android 7.1 (poziom 25 interfejsu API) lub starszy
System nie zarządza fokusem audio, a aplikacje zarządzają nim za pomocą funkcji requestAudioFocus()abandonAudioFocus().

Skupienie dźwięku na Androidzie 12 i nowszym

Aplikacja multimedialna lub gra, która korzysta z fokusowania dźwięku, nie powinna odtwarzać dźwięku po utracie fokusu. W Androidzie 12 (API na poziomie 31) i nowszym system wymusza takie zachowanie. Gdy aplikacja zażąda fokusu audio, a inna aplikacja ma fokus i odtwarza dźwięk, system wymusza wyciszenie odtwarzającej aplikacji. Dodanie efektu wygaszania zapewnia płynniejsze przejście z jednej aplikacji do drugiej.

Efekt wygaszania występuje, gdy są spełnione te warunki:

  1. Pierwsza aplikacja, która jest obecnie odtwarzana, spełnia wszystkie te kryteria:

  2. Druga aplikacja prosi o aktywność audio z ustawieniem AudioManager.AUDIOFOCUS_GAIN.

Gdy te warunki są spełnione, system audio wycisza pierwszą aplikację. Po zakończeniu wyciszania system powiadamia pierwszą aplikację o utracie fokusu. Odtwarzacze aplikacji pozostają wyciszone, dopóki aplikacja nie poprosi ponownie o aktywność audio.

Dotychczasowe zachowania związane z priorytetem dźwięku

Pamiętaj też o tych przypadkach, w których następuje zmiana źródła dźwięku.

Automatyczne wyciszanie

Automatyczne wyciszanie tła (tymczasowe zmniejszanie poziomu dźwięku jednej aplikacji, aby wyraźnie słyszeć inną) zostało wprowadzone w Androidzie 8.0 (poziom interfejsu API 26).

Dzięki temu, że system implementuje wyciszanie, nie musisz go implementować w swojej aplikacji.

Automatyczne wyciszanie następuje również wtedy, gdy powiadomienie dźwiękowe przejmuje fokus z odtwarzanej aplikacji. Początek odtwarzania powiadomienia jest zsynchronizowany z końcem wyciszania.

Automatyczne ściszanie następuje, gdy są spełnione te warunki:

  1. Pierwsza aktualnie odtwarzana aplikacja spełnia wszystkie te kryteria:

  2. Druga aplikacja prosi o aktywność audio za pomocą metody AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Gdy te warunki są spełnione, system audio wycisza wszystkich aktywnych odtwarzaczy pierwszej aplikacji, gdy druga aplikacja jest aktywna. Gdy druga aplikacja przestanie być aktywna, zostanie odpięta. Pierwsza aplikacja nie otrzymuje powiadomienia o utracie fokusu, więc nie musi nic robić.

Pamiętaj, że automatyczne wyciszanie nie jest wykonywane, gdy użytkownik słucha treści mówionych, ponieważ może wtedy przegapić część programu. Na przykład wskazówki głosowe dotyczące trasy nie są wyciszane.

Wyciszanie odtwarzania dźwięku podczas połączeń przychodzących

Niektóre aplikacje nie działają prawidłowo i nadal odtwarzają dźwięk podczas rozmów telefonicznych. W takiej sytuacji użytkownik musi znaleźć i wyciszyć lub zamknąć aplikację, która zakłóca połączenie, aby móc je usłyszeć. Aby temu zapobiec, system może wyciszyć dźwięk z innych aplikacji podczas połączenia przychodzącego. System wywołuje tę funkcję, gdy przychodzi połączenie telefoniczne, a aplikacja spełnia te warunki:

  • Aplikacja ma atrybut użycia AudioAttributes.USAGE_MEDIA lub AudioAttributes.USAGE_GAME.
  • Aplikacja przesłała udane żądanie fokusu dźwięku (dowolnego typu) i odtwarza dźwięk.

Jeśli aplikacja będzie odtwarzać treści podczas połączenia, odtwarzanie zostanie wyciszone do momentu zakończenia połączenia. Jeśli jednak aplikacja zacznie odtwarzać dźwięk podczas połączenia, nie zostanie wyciszona, ponieważ zakłada się, że użytkownik celowo rozpoczął odtwarzanie.

Skupienie dźwięku w Androidzie w wersji od 8.0 do 11

Począwszy od Androida 8.0 (interfejs API na poziomie 26), gdy wywołujesz funkcję requestAudioFocus(), musisz podać parametr AudioFocusRequest. Plik AudioFocusRequest zawiera informacje o kontekście audio i możliwościach aplikacji. System wykorzystuje te informacje do automatycznego zarządzania uzyskiwaniem i utratą fokusu audio. Aby zwolnić aktywność audio, wywołaj metodę abandonAudioFocusRequest() która przyjmuje też argument AudioFocusRequest. Używaj tej samej instancji AudioFocusRequest zarówno podczas wysyłania żądania, jak i rezygnowania z koncentracji.

Aby utworzyć AudioFocusRequest, użyj AudioFocusRequest.Builder. Żądanie fokusu musi zawsze określać typ żądania, dlatego typ jest uwzględniony w konstruktorze narzędzia do tworzenia. Użyj metod narzędzia do tworzenia, aby ustawić pozostałe pola żądania.

Pole FocusGain jest wymagane, a wszystkie pozostałe pola są opcjonalne.

MetodaUwagi
setFocusGain() To pole jest wymagane w każdym żądaniu. Przyjmuje te same wartości co durationHint używane w wywołaniu requestAudioFocus() przed Androidem 8.0:AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT,AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK lub AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
setAudioAttributes() AudioAttributes opisuje przypadek użycia aplikacji. System sprawdza je, gdy aplikacja uzyskuje i traci fokus audio. Atrybuty zastępują pojęcie typu strumienia. W Androidzie 8.0 (poziom interfejsu API 26) i nowszych typy strumieni dla wszystkich operacji innych niż sterowanie głośnością są wycofane. Użyj tych samych atrybutów w żądaniu fokusu, których używasz w odtwarzaczu audio (jak pokazano w przykładzie poniżej tej tabeli).

Użyj AudioAttributes.Builder, aby najpierw określić atrybuty, a następnie użyj tej metody, aby przypisać atrybuty do żądania.

Jeśli nie podasz tu żadnej wartości, AudioAttributes przyjmie domyślnie wartość AudioAttributes.USAGE_MEDIA.

setWillPauseWhenDucked() Gdy inna aplikacja poprosi o skupienie za pomocą funkcji AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, aplikacja, która ma skupienie, zwykle nie otrzymuje wywołania zwrotnego onAudioFocusChange(), ponieważ system może samodzielnie wyciszyć dźwięk. Jeśli chcesz wstrzymać odtwarzanie, a nie zmniejszyć głośność, wywołaj funkcję setWillPauseWhenDucked(true) i utwórz oraz ustaw OnAudioFocusChangeListener zgodnie z opisem w sekcji automatyczne zmniejszanie głośności.
setAcceptsDelayedFocusGain() Prośba o aktywność audio może się nie powieść, gdy jest ona zablokowana przez inną aplikację. Ta metoda umożliwia opóźnione uzyskanie aktywności: możliwość asynchronicznego uzyskania aktywności, gdy stanie się ona dostępna.

Pamiętaj, że opóźnione uzyskanie aktywności działa tylko wtedy, gdy w żądaniu audio podasz też AudioManager.OnAudioFocusChangeListener , ponieważ aplikacja musi otrzymać wywołanie zwrotne, aby wiedzieć, że aktywność została przyznana.

setOnAudioFocusChangeListener() Element OnAudioFocusChangeListener jest wymagany tylko wtedy, gdy w żądaniu podasz też element willPauseWhenDucked(true) lub setAcceptsDelayedFocusGain(true).

Są 2 metody ustawiania odbiornika: jedna z argumentem handlera i jedna bez niego. Obsługa to wątek, w którym działa odbiornik. Jeśli nie określisz modułu obsługi, zostanie użyty moduł obsługi powiązany z głównymLooper.

Ten przykład pokazuje, jak za pomocą AudioFocusRequest.Builder utworzyć AudioFocusRequest oraz wysłać żądanie i zrezygnować z fokusowania dźwięku:

Kotlin

// initializing variables for audio focus and playback management
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
    setAudioAttributes(AudioAttributes.Builder().run {
        setUsage(AudioAttributes.USAGE_GAME)
        setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        build()
    })
    setAcceptsDelayedFocusGain(true)
    setOnAudioFocusChangeListener(afChangeListener, handler)
    build()
}
val focusLock = Any()

var playbackDelayed = false
var playbackNowAuthorized = false

// requesting audio focus and processing the response
val res = audioManager.requestAudioFocus(focusRequest)
synchronized(focusLock) {
    playbackNowAuthorized = when (res) {
        AudioManager.AUDIOFOCUS_REQUEST_FAILED -> false
        AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
            playbackNow()
            true
        }
        AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> {
            playbackDelayed = true
            false
        }
        else -> false
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
override fun onAudioFocusChange(focusChange: Int) {
    when (focusChange) {
        AudioManager.AUDIOFOCUS_GAIN ->
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false
                    resumeOnFocusGain = false
                }
                playbackNow()
            }
        AudioManager.AUDIOFOCUS_LOSS -> {
            synchronized(focusLock) {
                resumeOnFocusGain = false
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying()
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // ... pausing or ducking depends on your app
        }
    }
}

Java

// initializing variables for audio focus and playback management
audioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
playbackAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
        .setAudioAttributes(playbackAttributes)
        .setAcceptsDelayedFocusGain(true)
        .setOnAudioFocusChangeListener(afChangeListener, handler)
        .build();
final Object focusLock = new Object();

boolean playbackDelayed = false;
boolean playbackNowAuthorized = false;

// requesting audio focus and processing the response
int res = audioManager.requestAudioFocus(focusRequest);
synchronized(focusLock) {
    if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
        playbackNowAuthorized = false;
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        playbackNowAuthorized = true;
        playbackNow();
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
        playbackDelayed = true;
        playbackNowAuthorized = false;
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
@Override
public void onAudioFocusChange(int focusChange) {
    switch (focusChange) {
        case AudioManager.AUDIOFOCUS_GAIN:
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false;
                    resumeOnFocusGain = false;
                }
                playbackNow();
            }
            break;
        case AudioManager.AUDIOFOCUS_LOSS:
            synchronized(focusLock) {
                resumeOnFocusGain = false;
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying();
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            // ... pausing or ducking depends on your app
            break;
        }
    }
}

Automatyczne wyciszanie

W Androidzie 8.0 (poziom interfejsu API 26), gdy inna aplikacja zażąda fokusu za pomocą AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, system może przyciszyć i przywrócić głośność bez wywoływania wywołania zwrotnego onAudioFocusChange() aplikacji.

Automatyczne ściszanie jest akceptowalnym zachowaniem w przypadku aplikacji do odtwarzania muzyki i filmów, ale nie jest przydatne podczas odtwarzania treści mówionych, np. w aplikacji do odtwarzania audiobooków. W takim przypadku aplikacja powinna wstrzymać odtwarzanie.

Jeśli chcesz, aby aplikacja wstrzymywała odtwarzanie, gdy zostanie poproszona o przyciszenie dźwięku, zamiast zmniejszać głośność, utwórz OnAudioFocusChangeListener z metodą wywołania zwrotnego onAudioFocusChange(), która implementuje żądane zachowanie wstrzymywania i wznawiania. Zadzwoń pod numer setOnAudioFocusChangeListener(), aby zarejestrować słuchacza, a następnie zadzwoń pod numer setWillPauseWhenDucked(true), aby poinformować system, że ma użyć Twojego wywołania zwrotnego zamiast automatycznego wyciszania.

Opóźnione uzyskanie ostrości

Czasami system nie może przyznać żądania fokusu audio, ponieważ jest on „zablokowany” przez inną aplikację, np. podczas rozmowy telefonicznej. W tym przypadku funkcja requestAudioFocus() zwraca wartość AUDIOFOCUS_REQUEST_FAILED. W takiej sytuacji aplikacja nie powinna odtwarzać dźwięku, ponieważ nie uzyskała fokusu.

Metoda setAcceptsDelayedFocusGain(true), która umożliwia aplikacji asynchroniczne obsługiwanie żądania fokusu. Gdy ta flaga jest ustawiona, żądanie wysłane, gdy fokus jest zablokowany, zwraca wartość AUDIOFOCUS_REQUEST_DELAYED. Gdy warunek, który zablokował fokus audio, przestanie istnieć (np. po zakończeniu połączenia telefonicznego), system przyzna oczekującą prośbę o fokus i wywoła funkcję onAudioFocusChange(), aby powiadomić aplikację.

Aby poradzić sobie z opóźnionym uzyskaniem fokusu, musisz utworzyć OnAudioFocusChangeListener z metodą wywołania zwrotnego onAudioFocusChange(), która implementuje żądane działanie, i zarejestrować odbiornik, wywołując setOnAudioFocusChangeListener().

Aktywność audio w Androidzie 7.1 i starszych

Gdy wywołujesz requestAudioFocus() musisz podać wskazówkę dotyczącą czasu trwania, która może zostać uwzględniona przez inną aplikację, która jest obecnie aktywna i odtwarza:

  • Poproś o stałe skupienie dźwięku (AUDIOFOCUS_GAIN), jeśli planujesz odtwarzać dźwięk w najbliższej przyszłości (np. podczas odtwarzania muzyki) i oczekujesz, że poprzedni posiadacz skupienia dźwięku przestanie odtwarzać.
  • Poproś o przejściowe skupienie (AUDIOFOCUS_GAIN_TRANSIENT), gdy chcesz odtwarzać dźwięk tylko przez krótki czas i oczekujesz, że poprzedni odtwarzacz wstrzyma odtwarzanie.
  • Poproś o przejściowe skupienie z wyciszeniem (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK), aby wskazać, że zamierzasz odtwarzać dźwięk tylko przez krótki czas i że poprzedni właściciel skupienia może kontynuować odtwarzanie, jeśli „wyciszy” (obniży) poziom dźwięku. Oba wyjścia audio są miksowane w strumieniu audio. Ściszanie jest szczególnie przydatne w przypadku aplikacji, które korzystają ze strumienia audio z przerwami, np. w przypadku wskazówek dojazdu.

Metoda requestAudioFocus() wymaga też AudioManager.OnAudioFocusChangeListener. Ten odbiornik powinien być utworzony w tej samej aktywności lub usłudze, która jest właścicielem sesji multimedialnej. Implementuje wywołanie zwrotne onAudioFocusChange(), które aplikacja otrzymuje, gdy inna aplikacja uzyska lub utraci fokus audio.

Poniższy fragment kodu wysyła prośbę o stałe skupienie dźwięku na strumieniu STREAM_MUSIC i rejestruje OnAudioFocusChangeListener do obsługi kolejnych zmian w skupieniu dźwięku. (O obserwatorze zmian piszemy w sekcji Reagowanie na zmianę fokusu dźwięku).

Kotlin

audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
lateinit var afChangeListener AudioManager.OnAudioFocusChangeListener

...
// Request audio focus for playback
val result: Int = audioManager.requestAudioFocus(
        afChangeListener,
        // Use the music stream.
        AudioManager.STREAM_MUSIC,
        // Request permanent focus.
        AudioManager.AUDIOFOCUS_GAIN
)

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

Java

AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener afChangeListener;

...
// Request audio focus for playback
int result = audioManager.requestAudioFocus(afChangeListener,
                             // Use the music stream.
                             AudioManager.STREAM_MUSIC,
                             // Request permanent focus.
                             AudioManager.AUDIOFOCUS_GAIN);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

Po zakończeniu odtwarzania kliknij Zadzwoń abandonAudioFocus().

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

// Abandon audio focus when playback complete
audioManager.abandonAudioFocus(afChangeListener);

Informuje to system, że nie potrzebujesz już skupienia, i wyrejestrowuje powiązany OnAudioFocusChangeListener. Jeśli zażądasz tymczasowego skupienia, powiadomisz aplikację, która została wstrzymana lub wyciszona, że może kontynuować odtwarzanie lub przywrócić głośność.

Reagowanie na zmianę fokusu dźwięku

Gdy aplikacja uzyska fokus audio, musi być w stanie go zwolnić, gdy inna aplikacja poprosi o fokus audio dla siebie. W takiej sytuacji aplikacja odbiera wywołanie metody onAudioFocusChange()AudioFocusChangeListener, która została określona, gdy aplikacja wywołała requestAudioFocus().

Parametr focusChange przekazywany do onAudioFocusChange() wskazuje rodzaj wprowadzanej zmiany. Odpowiada to wskazówce dotyczącej czasu trwania używanej przez aplikację, która uzyskuje fokus. Aplikacja powinna odpowiednio reagować.

Przejściowa utrata ostrości
Jeśli zmiana fokusu jest tymczasowa (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK lubAUDIOFOCUS_LOSS_TRANSIENT), aplikacja powinna wyciszyć dźwięk (jeśli nie korzystasz z automatycznego wyciszania) lub wstrzymać odtwarzanie, ale w innych przypadkach zachować ten sam stan.

Podczas przejściowej utraty aktywności audio należy nadal monitorować zmiany aktywności audio i być gotowym do wznowienia normalnego odtwarzania po odzyskaniu aktywności. Gdy aplikacja blokująca przestanie być aktywna, otrzymasz wywołanie zwrotne (AUDIOFOCUS_GAIN). W tym momencie możesz przywrócić normalny poziom głośności lub ponownie uruchomić odtwarzanie.

Trwała utrata ostrości
Jeśli utrata fokusu audio jest trwała (AUDIOFOCUS_LOSS), inna aplikacja odtwarza dźwięk. Aplikacja powinna natychmiast wstrzymać odtwarzanie, ponieważ nigdy nie otrzyma wywołania zwrotnego AUDIOFOCUS_GAIN. Aby ponownie uruchomić odtwarzanie, użytkownik musi wykonać wyraźną czynność, np. nacisnąć przycisk odtwarzania w powiadomieniu lub interfejsie aplikacji.

Poniższy fragment kodu pokazuje, jak wdrożyć funkcję OnAudioFocusChangeListener i jej wywołanie zwrotne onAudioFocusChange(). Zwróć uwagę na użycie Handler, aby opóźnić wywołanie zwrotne zatrzymania w przypadku trwałej utraty fokusu audio.

Kotlin

private val handler = Handler()
private val afChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
    when (focusChange) {
        AudioManager.AUDIOFOCUS_LOSS -> {
            // Permanent loss of audio focus
            // Pause playback immediately
            mediaController.transportControls.pause()
            // Wait 30 seconds before stopping playback
            handler.postDelayed(delayedStopRunnable, TimeUnit.SECONDS.toMillis(30))
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            // Pause playback
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // Lower the volume, keep playing
        }
        AudioManager.AUDIOFOCUS_GAIN -> {
            // Your app has been granted audio focus again
            // Raise volume to normal, restart playback if necessary
        }
    }
}

Java

private Handler handler = new Handler();
AudioManager.OnAudioFocusChangeListener afChangeListener =
  new AudioManager.OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
      if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
        // Permanent loss of audio focus
        // Pause playback immediately
        mediaController.getTransportControls().pause();
        // Wait 30 seconds before stopping playback
        handler.postDelayed(delayedStopRunnable,
          TimeUnit.SECONDS.toMillis(30));
      }
      else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
        // Pause playback
      } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
        // Lower the volume, keep playing
      } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
        // Your app has been granted audio focus again
        // Raise volume to normal, restart playback if necessary
      }
    }
  };

Obsługa używa Runnable, która wygląda tak:

Kotlin

private var delayedStopRunnable = Runnable {
    mediaController.transportControls.stop()
}

Java

private Runnable delayedStopRunnable = new Runnable() {
    @Override
    public void run() {
        getMediaController().getTransportControls().stop();
    }
};

Aby mieć pewność, że opóźnione zatrzymanie nie zostanie uruchomione, jeśli użytkownik ponownie uruchomi odtwarzanie, wywołaj funkcję mHandler.removeCallbacks(mDelayedStopRunnable) w odpowiedzi na wszelkie zmiany stanu. Na przykład wywołaj removeCallbacks() w funkcji wywołania zwrotnego onPlay(), onSkipToNext() itp. Tę metodę należy też wywołać w funkcji wywołania zwrotnego usługi onDestroy() podczas czyszczenia zasobów używanych przez usługę.