Zarządzaj aktywnością audio

2 lub więcej aplikacji na Androida może odtwarzać dźwięk na tym samym strumieniu wyjściowym jednocześnie, a system miksuje wszystko razem. Chociaż jest to technicznie imponujące, może to bardzo denerwować użytkownika. Aby uniknąć odtwarzania wszystkich aplikacji muzycznych jednocześnie, Android wprowadza funkcję fokusowania dźwięku. W danym momencie tylko jedna aplikacja może mieć dostęp do dźwięku.

Gdy aplikacja potrzebuje wyprowadzić dźwięk, powinna poprosić o skupienie na dźwięku. Gdy ma fokus, może odtwarzać dźwięk. Jednak po uzyskaniu kontroli nad dźwiękiem możesz nie mieć możliwości utrzymania jej do końca odtwarzania. Inna aplikacja może poprosić o utrzymanie fokusu, co zablokuje Twoje ustawienie. W takim przypadku aplikacja powinna wstrzymać odtwarzanie lub obniżyć głośność, aby użytkownicy mogli lepiej usłyszeć nowe źródło dźwięku.

Przed Androidem 12 (poziom interfejsu API 31) system nie zarządzał skupieniem na dźwięku. Deweloperzy aplikacji są zachęcani do przestrzegania wytycznych dotyczących dźwięku w tle, ale jeśli aplikacja nadal odtwarza dźwięk nawet po utracie dźwięku w tle na urządzeniu z Androidem 11 (poziom interfejsu API 30) lub starszym, system nie może temu zapobiec. Takie działanie aplikacji obniża jednak komfort użytkowników i często powoduje, że odinstalowują oni aplikację.

Dobrze zaprojektowana aplikacja do odtwarzania dźwięku powinna zarządzać skupieniem dźwięku zgodnie z tymi ogólnymi wskazówkami:

  • Zadzwoń pod numer requestAudioFocus() tuż przed rozpoczęciem gry i sprawdź, czy połączenie zostanie odebrane.AUDIOFOCUS_REQUEST_GRANTED Wykonaj połączenie do requestAudioFocus() w ramach wywołania zwrotnego onPlay() sesji multimediów.

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

  • Gdy odtwarzanie zostanie zatrzymane (np. gdy aplikacja nie ma już nic do odtworzenia), zatrzymaj tryb dźwięku w tle. Aplikacja nie musi tracić fokusu na dźwięku, jeśli użytkownik wstrzyma odtwarzanie, ale może wznowić odtwarzanie później.

  • Użyj atrybutu AudioAttributes, aby opisać typ odtwarzanego dźwięku w aplikacji. Na przykład w przypadku aplikacji odtwarzających mowę wpisz: CONTENT_TYPE_SPEECH.

Tryb skupienia dźwięku jest obsługiwany inaczej w zależności od wersji Androida:

Android 12 (poziom 31 interfejsu API) lub nowszy
Tryb dźwięku jest zarządzany przez system. System powoduje, że odtwarzanie dźwięku z aplikacji zostaje wyciszone, gdy inna aplikacja prosi o uaktywnienie dźwięku. System wycisza też odtwarzanie dźwięku podczas połączenia przychodzącego.
Android 8.0 (poziom 26 interfejsu API) do Androida 11 (poziom 30 interfejsu API)
System nie zarządza dźwiękiem w tle, ale zawiera pewne zmiany wprowadzone w Androidzie 8.0 (poziom interfejsu API 26).
Android 7.1 (poziom interfejsu API 25) lub starszy
System nie zarządza dźwiękiem w tle, a aplikacje zarządzają dźwiękiem w tle za pomocą funkcji requestAudioFocus()abandonAudioFocus().

Tryb skupienia na dźwięku w Androidzie 12 i nowszych

Aplikacja do multimediów lub gra, która korzysta z fokusa dźwięku, nie powinna odtwarzać dźwięku po utracie fokusu. W Androidzie 12 (poziom interfejsu API 31) i nowszym system wymusza takie działanie. Gdy aplikacja prosi o skupienie się na dźwięku, gdy inna aplikacja jest aktywna i odtwarza dźwięk, system powoduje zniknięcie dźwięku z innej aplikacji. Dodanie efektu znikania zapewnia płynniejsze przejście z jednej aplikacji do drugiej.

To zachowanie występuje, gdy są spełnione te warunki:

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

  2. Druga aplikacja prosi o aktywność audio z użyciem AudioManager.AUDIOFOCUS_GAIN.

Gdy te warunki są spełnione, system audio powoduje wygładzenie dźwięku w pierwszej aplikacji. Po zakończeniu wygładzania system informuje pierwszą aplikację o utracie fokusu. Odtwarzacze aplikacji pozostają wyciszone, dopóki aplikacja ponownie nie poprosi o uprawnienia.

Dotychczasowe zachowania dotyczące priorytetu dźwięku

Musisz też pamiętać o tych innych przypadkach, które wiążą się ze zmianą punktu skupienia dźwięku.

Automatyczne wyciszanie

Automatyczne wyciszanie (tymczasowe zmniejszenie poziomu głośności jednej aplikacji, aby wyraźnie słyszeć inną) zostało wprowadzone w Androidzie 8.0 (poziom interfejsu API 26).

Jeśli system obsługuje wyciszanie, nie musisz go implementować w aplikacji.

Automatyczne stłumienie występuje też wtedy, gdy powiadomienie dźwiękowe przejmuje kontrolę nad odtwarzaniem z aplikacji. Początek odtwarzania powiadomienia jest zsynchronizowany z końcem rampy stłumienia.

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

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

  2. Druga aplikacja prosi o uprawnienia dotyczące dźwięku: AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Gdy te warunki są spełnione, system audio wycisza wszystkich aktywnych odtwarzaczy w pierwszej aplikacji, gdy druga aplikacja ma fokus. Gdy druga aplikacja przestaje być w centrum uwagi, powoduje to odblokowanie tych elementów. Pierwsza aplikacja nie jest powiadamiana, gdy traci na chwilę fokus, więc nie musi nic robić.

Pamiętaj, że automatyczne wyciszanie nie jest wykonywane, gdy użytkownik słucha treści mowy, ponieważ może on przegapić część programu. Na przykład wskazówki głosowe dotyczące wskazówek dojazdu nie są wyciszone.

wyciszyć bieżące odtwarzanie dźwięku podczas połączeń telefonicznych,

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

  • Aplikacja ma atrybut użycia AudioAttributes.USAGE_MEDIA lub AudioAttributes.USAGE_GAME.
  • Aplikacja poprosiła o skupienie uwagi na dźwięku (dowolne skupienie uwagi) i odtwarza dźwięk.

Jeśli aplikacja nadal odtwarza dźwięk podczas rozmowy, zostanie wyciszona do jej zakończenia. Jeśli jednak podczas rozmowy włączy się odtwarzanie aplikacji, odtwarzacz nie zostanie wyciszony, ponieważ zakładamy, że użytkownik celowo rozpoczął odtwarzanie.

Koncentracja dźwięku w Androidzie 8.0–11

Począwszy od Androida 8.0 (poziom interfejsu API 26), podczas wywoływania funkcji requestAudioFocus() musisz podać parametr AudioFocusRequest. AudioFocusRequestzawiera informacje o kontekście dźwięku i możliwościach aplikacji. System używa tych informacji do automatycznego zarządzania wzmocnieniem i utratą skupienia na dźwięku. Aby zwolnić aktywność audio, wywołaj metodę abandonAudioFocusRequest(), która również przyjmuje argument AudioFocusRequest. Używaj tej samej instancji AudioFocusRequest zarówno podczas żądania, jak i opuszczania trybu pełnej koncentracji.

Aby utworzyć AudioFocusRequest, użyj AudioFocusRequest.Builder. Ponieważ żądanie fokusu musi zawsze określać typ żądania, typ jest uwzględniany w konstruktorze kreatora. Aby ustawić inne pola żądania, użyj metod konstruktora.

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

MetodaUwagi
setFocusGain() To pole jest wymagane w każdej prośbie. Przyjmuje te same wartości co durationHint używane w wywołaniu requestAudioFocus() w systemie Android 8.0: AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK lub AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
setAudioAttributes() AudioAttributes określa przypadek użycia aplikacji. System sprawdza te wartości, gdy aplikacja zyskuje i traci nacisk dźwiękowy. Atrybuty zastępują pojęcie typu strumienia. W Androidzie w wersji 8.0 (poziom interfejsu API 26) i później typy strumienia dla operacji innych niż sterowanie głośnością są wycofane. Użyj w żądaniu fokusa tych samych atrybutów, których używasz w odtwarzaczu audio (jak pokazano w przykładzie po tej tabeli).

Najpierw użyj elementu AudioAttributes.Builder, aby określić atrybuty, a potem użyj tej metody, aby przypisać atrybuty do żądania.

Jeśli nie podasz wartości parametru AudioAttributes, zostanie użyta wartość domyślna AudioAttributes.USAGE_MEDIA.

setWillPauseWhenDucked() Gdy inna aplikacja poprosi o uzyskanie fokusu za pomocą wywołania AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, aplikacja, która ma fokus, zwykle nie otrzymuje wywołania onAudioFocusChange(), ponieważ system może sam wykonać duckowanie. Jeśli chcesz wstrzymać odtwarzanie, a nie ściszyć głośności, wywołaj funkcję setWillPauseWhenDucked(true) i utwórz oraz ustaw funkcję OnAudioFocusChangeListener, jak opisano w sekcji Automatyczne ściszanie głośności.
setAcceptsDelayedFocusGain() Prośba o uzyskanie kontroli nad dźwiękiem może się nie powieść, jeśli kontrolę tę ma inna aplikacja. Ta metoda umożliwia opóźnione przejęcie kontroli: asynchroniczne przejęcie kontroli, gdy tylko stanie się to możliwe.

Pamiętaj, że opóźnione wzmocnienie dźwięku działa tylko wtedy, gdy w prośbie o dostęp do mikrofonu podasz też parametr AudioManager.OnAudioFocusChangeListener, ponieważ aplikacja musi otrzymać wywołanie zwrotne, aby wiedzieć, że została przyznana funkcja sterowania dźwiękiem.

setOnAudioFocusChangeListener() Właściwość OnAudioFocusChangeListener jest wymagana tylko wtedy, gdy w żądaniu określono też willPauseWhenDucked(true) lub setAcceptsDelayedFocusGain(true).

Istnieją 2 metody ustawiania listenera: jedna z argumentem handlera, a druga bez niego. Obsługa to wątek, w którym działa odbiornik. Jeśli nie określisz uchwytu, zostanie użyty uchwyt powiązany z głównym Looper.

Ten przykład pokazuje, jak użyć AudioFocusRequest.Builder do utworzenia AudioFocusRequest oraz jak poprosić o skupienie się na dźwięku i jego zaniechanie:

KotlinJava
// 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
        }
    }
}
// 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 poprosi o skupienie uwagi za pomocą AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, system może wyciszyć i przywrócić głośność bez wywoływania funkcji onAudioFocusChange() aplikacji.

Automatyczne wyciszanie jest dopuszczalne w przypadku aplikacji do odtwarzania muzyki i filmów, ale nie jest przydatne podczas odtwarzania treści mówionej, np. w aplikacji z książkami audio. W takim przypadku aplikacja powinna się wstrzymać.

Jeśli chcesz, aby aplikacja wstrzymywała się, gdy poprosisz o ściszenie, zamiast zmniejszać głośność, utwórz OnAudioFocusChangeListener z metodą wywołania onAudioFocusChange(), która wdraża żądane zachowanie wstrzymywania i wznawiania. Aby zarejestrować odbiorcę, wywołaj funkcję setOnAudioFocusChangeListener(), a aby wskazać systemowi, że ma użyć wywołania zwrotnego zamiast automatycznego wyciszania, wywołaj funkcję setWillPauseWhenDucked(true).

Opóźnione wzmocnienie ostrości

Czasami system nie może spełnić prośby o skupienie się na dźwięku, ponieważ jest ono „zablokowane” przez inną aplikację, np. podczas rozmowy telefonicznej. W tym przypadku funkcja requestAudioFocus() zwraca wartość AUDIOFOCUS_REQUEST_FAILED. W takim przypadku aplikacja nie powinna odtwarzać dźwięku, ponieważ nie została skoncentrowana.

Metoda setAcceptsDelayedFocusGain(true), która umożliwia aplikacji asynchroniczne obsługiwanie żądania dotyczącego skupienia. Gdy ta flaga jest ustawiona, żądanie wysłane, gdy fokus jest zablokowany, zwraca wartość AUDIOFOCUS_REQUEST_DELAYED. Gdy warunek, który zablokował dźwięk, przestanie obowiązywać, np. gdy połączenie telefoniczne się zakończy, system zaakceptuje oczekującą prośbę o włączenie trybu dźwiękowego i wywoła funkcję onAudioFocusChange(), aby powiadomić Twoją aplikację.

Aby obsłużyć opóźnione uzyskanie fokusu, musisz utworzyć OnAudioFocusChangeListener z metodą wywołania onAudioFocusChange(), która implementuje pożądane działanie i rejestruje słuchacza, wywołując setOnAudioFocusChangeListener().

Aktywność audio w Androidzie 7.1 i starszych

Gdy wywołujesz elementrequestAudioFocus(), musisz podać podpowiedź dotyczącą czasu trwania, która może być uwzględniona przez inną aplikację, która jest obecnie aktywna i odtwarzana:

  • Poproś o trwałe skupienie na dźwięku (AUDIOFOCUS_GAIN), jeśli planujesz odtwarzać dźwięk przez dłuższy czas (np. podczas odtwarzania muzyki) i chcesz, aby poprzedni posiadacz skupienia na dźwięku zaprzestał odtwarzania.
  • Poproś o przejście w tryb przejściowy (AUDIOFOCUS_GAIN_TRANSIENT), gdy chcesz odtwarzać dźwięk tylko przez krótki czas i oczekujesz, że poprzedni właściciel wstrzyma odtwarzanie.
  • Żądanie przejściowego skupienia z podbiciem (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK), aby wskazać, że dźwięk ma być odtwarzany tylko przez krótki czas i że poprzedni właściciel skupienia może nadal odtwarzać dźwięk, jeśli zmniejszy jego poziom. Oba wyjścia audio są miksowane w strumień audio. Ducking jest szczególnie przydatne w przypadku aplikacji, które od czasu do czasu używają strumienia audio, np. w przypadku wskazówek głosowych do nawigacji.

Metoda requestAudioFocus() wymaga też AudioManager.OnAudioFocusChangeListener. Ten odbiornik powinien zostać utworzony w tej samej aktywności lub usłudze, która jest właścicielem sesji multimedialnej. Implementuje ona funkcję wywołania zwrotnego onAudioFocusChange(), którą Twoja aplikacja otrzymuje, gdy inna aplikacja uzyska lub straci skupienie na dźwięku.

Ten fragment kodu prosi o stałe skupienie się na strumieniu STREAM_MUSIC i rejestruje zdarzenie OnAudioFocusChangeListener, aby obsługiwać kolejne zmiany w fokusie dźwięku. (omawiamy go w artykule Reagowanie na zmianę dźwięku).

KotlinJava
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
}
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 zadzwoń pod numer abandonAudioFocus().

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

To powiadomi system, że nie potrzebujesz już fokusa, i odpisze powiązany z nim OnAudioFocusChangeListener. Jeśli poprosisz o tymczasowe skupienie, aplikacja, która została wstrzymana lub wyciszona, otrzyma powiadomienie, że może wznowić odtwarzanie lub przywrócić głośność.

Odpowiadanie na zmianę ustawień dźwięku

Gdy aplikacja uzyska dostęp do dźwięku, musi mieć możliwość zrezygnowania z niego, gdy inna aplikacja poprosi o to. W takim przypadku aplikacja otrzymuje wywołanie metody onAudioFocusChange() w pakiecie AudioFocusChangeListener, który został określony podczas wywołania metody requestAudioFocus().

Parametr focusChange przekazany do funkcji onAudioFocusChange() wskazuje rodzaj wprowadzanej zmiany. Odpowiada on podpowiedzi dotyczącej czasu używanej przez aplikację, która przejmuje fokus. Aplikacja powinna odpowiednio reagować.

Przejściowa utrata ostrości
Jeśli zmiana punktu skupienia jest przejściowa (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK lub AUDIOFOCUS_LOSS_TRANSIENT), aplikacja powinna wyciszyć dźwięk (jeśli nie korzystasz z automatycznego wyciszania) lub wstrzymać odtwarzanie, ale w pozostałych aspektach zachować ten sam stan.

Podczas chwilowej utraty aktywności audio należy nadal monitorować zmiany w aktywności audio i być gotowym do wznowienia normalnego odtwarzania po jej przywróceniu. Gdy aplikacja blokująca przestanie być aktywna, otrzymasz wywołanie zwrotne (AUDIOFOCUS_GAIN). W tym momencie możesz przywrócić głośność do normalnego poziomu lub wznowić odtwarzanie.

Utrata ostrości
Jeśli utrata fokusu dźwięku 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 wznowić odtwarzanie, użytkownik musi wykonać wyraźną czynność, np. nacisnąć przycisk odtwarzania w powiadomieniu lub interfejsie aplikacji.

Ten fragment kodu pokazuje, jak zaimplementować funkcję OnAudioFocusChangeListener i jej wywołanie zwrotne onAudioFocusChange(). Zwróć uwagę na użycie funkcji Handler, aby opóźnić wywołanie zwrotne stop w przypadku trwałej utraty fokusu dźwiękowego.

KotlinJava
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
        }
    }
}
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óry wygląda tak:

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

Aby opóźnione zatrzymanie nie zostało uruchomione, jeśli użytkownik ponownie uruchomi odtwarzanie, wywołaj funkcję mHandler.removeCallbacks(mDelayedStopRunnable) w odpowiedzi na zmiany stanu. Na przykład wywołaj removeCallbacks() w metodach onPlay(), onSkipToNext() itp. w wywołaniu zwrotnym. Należy też wywołać tę metodę w wywołaniu zwrotnym onDestroy() usługi podczas czyszczenia zasobów używanych przez usługę.