Co najmniej 2 aplikacje na Androida mogą jednocześnie odtwarzać dźwięk w tym samym strumieniu wyjściowym, a system miksuje wszystko razem. Technicznie to imponujące, ale może być bardzo irytujące. Aby uniknąć odtwarzania wszystkich aplikacji muzycznych w tym samym czasie, Android wprowadza fokus dźwiękowy. Tylko 1 aplikacja może w danym momencie utrzymywać aktywność audio.
Gdy aplikacja musi generować dźwięk, powinna prosić o zaznaczenie dźwięku. Może też odtwarzać dźwięk, gdy ma ostrość. Gdy jednak skupisz się na dźwięku, może nie udać się go zachować do czasu zakończenia odtwarzania. Inna aplikacja może poprosić o zaznaczenie, co uniemożliwia odtwarzanie dźwięku. W takiej sytuacji aplikacja powinna wstrzymać odtwarzanie lub zmniejszyć głośność, by użytkownicy mogli łatwiej usłyszeć nowe źródło dźwięku.
Przed Androidem 12 (poziom interfejsu API 31) system nie zarządza dźwiękiem. Dlatego zachęcamy deweloperów aplikacji do przestrzegania wskazówek dotyczących skupienia na dźwięku. Jeśli aplikacja będzie odtwarzać się głośno nawet po utracie dźwięku na urządzeniach z Androidem 11 (poziom interfejsu API 30) lub niższym, system nie może temu zapobiec. Takie działanie aplikacji zmniejsza jednak komfort użytkowników i często może doprowadzić do odinstalowania wadliwej aplikacji.
Dobrze zaprojektowana aplikacja audio powinna regulować sterowanie dźwiękiem zgodnie z tymi ogólnymi wytycznymi:
Wywołaj
requestAudioFocus()
bezpośrednio przed rozpoczęciem odtwarzania i sprawdź, czy wywołanie zwracaAUDIOFOCUS_REQUEST_GRANTED
. Wykonaj wywołanierequestAudioFocus()
w wywołaniu zwrotnymonPlay()
w sesji multimediów.Gdy inna aplikacja aktywuje sterowanie dźwiękiem, zatrzymuje lub wstrzymuje odtwarzanie albo wycisza (czyli ścisza) głośność.
Gdy odtwarzanie się zatrzyma (np. gdy w aplikacji nie ma już nic do odtworzenia), porzuć aktywność audio. Aplikacja nie musi porzucać dźwięku, jeśli użytkownik wstrzyma odtwarzanie, ale może wznowić odtwarzanie później.
Użyj
AudioAttributes
, aby opisać rodzaj dźwięku odtwarzanego w aplikacji. Na przykład w przypadku aplikacji do odtwarzania mowy podaj wartośćCONTENT_TYPE_SPEECH
.
Tryb audio jest obsługiwany w różny sposób w zależności od wersji Androida:
- Android 12 (poziom interfejsu API 31) lub nowszy,
- Systemem audio zarządza system. System wymusza na słuchaniu odtwarzanie dźwięku z aplikacji, gdy inna aplikacja prosi o zaznaczenie dźwięku. Po otrzymaniu połączenia przychodzącego system wyciszy też odtwarzanie dźwięku.
- Android od 8.0 (poziom interfejsu API 26) do 11 (poziom API 30)
- System audio nie zarządza dźwiękiem, ale obejmuje pewne zmiany wprowadzone w Androidzie 8.0 (poziom API 26).
- Android 7.1 (poziom interfejsu API 25) lub starszy
- System audio nie zarządza dźwiękiem, a aplikacje zarządzają sterowaniem dźwiękiem za pomocą interfejsów
requestAudioFocus()
iabandonAudioFocus()
.
Skupienie się na dźwięku na Androidzie 12 i nowszych
Aplikacja do multimediów lub gier, która korzysta z dźwięku, nie powinna odtwarzać dźwięku, gdy straci ostrość. W Androidzie 12 (poziom interfejsu API 31) i nowszych system wymusza to zachowanie. Gdy aplikacja prosi o zaznaczenie dźwięku w czasie, gdy inna aplikacja jest na nim aktywna, system wymusza jej wyciszenie. Dodanie zanikania zapewnia płynniejsze przejście między aplikacjami.
Zanikanie, które ma miejsce, gdy są spełnione te warunki:
Pierwsza, obecnie odtwarzana aplikacja, spełnia wszystkie te kryteria:
- Aplikacja ma atrybut wykorzystania
AudioAttributes.USAGE_MEDIA
lubAudioAttributes.USAGE_GAME
. - Aplikacja wysłała żądanie ostrości audio za pomocą aplikacji
AudioManager.AUDIOFOCUS_GAIN
. - Aplikacja nie odtwarza dźwięku z typem treści
AudioAttributes.CONTENT_TYPE_SPEECH
.
- Aplikacja ma atrybut wykorzystania
Druga aplikacja prosi o skupienie audio za pomocą
AudioManager.AUDIOFOCUS_GAIN
.
Gdy zostaną spełnione te warunki, system audio wycisza pierwszą aplikację. Na końcu wyciszania system powiadamia pierwszą aplikację o utracie ostrości. Odtwarzacze w aplikacji pozostaną wyciszone, dopóki aplikacja ponownie nie poprosi o zaznaczenie dźwięku.
Dotychczasowe zachowania dotyczące dźwięku
Musisz też pamiętać o innych przypadkach, w których trzeba zmienić tryb audio.
Automatyczne wyciszanie
Automatyczne wyciszanie (tymczasowe zmniejszanie poziomu dźwięku w jednej aplikacji, by można było wyraźnie słyszeć drugą) zostało wprowadzone w Androidzie 8.0 (poziom interfejsu API 26).
Gdy system zaimplementuje wyciszanie tła, nie musisz go blokować w aplikacji.
Automatyczne wyciszanie ma miejsce również wtedy, gdy aktywne jest powiadomienie dźwiękowe z odtwarzanej aplikacji. Rozpoczęcie odtwarzania powiadomienia jest zsynchronizowane z końcem wyciszania.
Automatyczne wyciszanie ma miejsce, gdy spełnione są te warunki:
Pierwsza aplikacja, która jest obecnie uruchomiona, spełnia wszystkie te kryteria:
- Aplikacja zażądała skupienia dźwięku przy dowolnym wzroście fokusu.
- Aplikacja nie odtwarza dźwięku z typem treści
AudioAttributes.CONTENT_TYPE_SPEECH
. - Aplikacja nie ustawiła parametru
AudioFocusRequest.Builder.setWillPauseWhenDucked(true)
.
Druga aplikacja prosi o skupienie audio za pomocą
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
Gdy te warunki są spełnione, system audio wycisza wszystkie aktywne odtwarzacze w pierwszej aplikacji, a druga jest na nich skupiona. Gdy druga aplikacja zniknie, nie będzie już działała. Pierwsza aplikacja nie otrzymuje powiadomienia, gdy przestaje być aktywna, więc nie musi nic robić.
Pamiętaj, że automatyczne wyciszanie nie jest wykonywane, gdy użytkownik słucha treści głosowych, ponieważ może przegapić część programu. Na przykład wskazówki głosowe we wskazówkach dojazdu nie są zasłonięte.
Wycisz bieżące odtwarzanie dźwięku przychodzących połączeń telefonicznych
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ć aplikację, która narusza zasady, lub ją zamknąć, aby usłyszeć jego połączenie. Aby temu zapobiec, system może wyciszyć dźwięk z innych aplikacji, gdy ktoś do Ciebie dzwoni. System wywołuje tę funkcję, gdy ktoś odbiera połączenie telefoniczne, a aplikacja spełnia te warunki:
- Aplikacja ma atrybut wykorzystania
AudioAttributes.USAGE_MEDIA
lubAudioAttributes.USAGE_GAME
. - Aplikacja zażądała ostrości audio (dowolnego wzmocnienia ostrości) i odtwarza dźwięk.
Jeśli podczas rozmowy aplikacja będzie nadal odtwarzana, jej odtwarzanie zostanie wyciszone do czasu zakończenia połączenia. Jeśli jednak aplikacja zacznie odtwarzać treści w trakcie połączenia, odtwarzacz nie zostanie wyciszony, jeśli użytkownik założy, że celowo rozpoczął odtwarzanie.
Funkcje audio w Androidzie 8.0–11
Począwszy od Androida 8.0 (poziom interfejsu API 26) podczas wywoływania requestAudioFocus()
musisz podać parametr AudioFocusRequest
. AudioFocusRequest
zawiera informacje o kontekście i funkcjach audio aplikacji. System używa tych informacji do automatycznego zarządzania poziomem skupienia i utratą dźwięku. Aby zwolnić aktywny element audio, wywołaj metodę abandonAudioFocusRequest()
, która również przyjmuje jako argument AudioFocusRequest
. Używaj tej samej instancji AudioFocusRequest
zarówno w przypadku wysyłania żądań, jak i porzucania zaznaczenia.
Aby utworzyć AudioFocusRequest
, użyj AudioFocusRequest.Builder
. Żądanie zaznaczenia zawsze musi określać typ żądania, dlatego jest on uwzględniany w konstruktorze. Użyj metod kreatora, aby ustawić inne pola żądania.
Pole FocusGain
jest wymagane, wszystkie pozostałe pola są opcjonalne.
Metoda | Uwagi |
---|---|
setFocusGain()
|
To pole jest wymagane w każdym żądaniu. Przyjmuje te same wartości co durationHint w wywołaniu usługi requestAudioFocus() w wersji wcześniejszej niż Android 8.0: AUDIOFOCUS_GAIN , AUDIOFOCUS_GAIN_TRANSIENT , AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK lub AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE .
|
setAudioAttributes()
|
AudioAttributes opisuje przypadek użycia Twojej aplikacji. System analizuje te przypadki, gdy aplikacja zwiększa lub traci koncentrację audio. Atrybuty zastępują pojęcie typu strumienia. W Androidzie 8.0 (poziom interfejsu API 26) i nowszych typy strumieni na potrzeby wszystkich operacji innych niż regulacja głośności są wycofywane. W żądaniu zaznaczenia użyj tych samych atrybutów co w odtwarzaczu audio (jak pokazano w przykładzie poniżej tej tabeli).
Użyj
Jeśli nie podasz żadnej wartości, |
setWillPauseWhenDucked()
|
Gdy inna aplikacja prosi o koncentrację za pomocą funkcji AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK , zaznaczona aplikacja zwykle nie otrzymuje wywołania zwrotnego onAudioFocusChange() , ponieważ system je wycisza. Jeśli chcesz wstrzymać odtwarzanie zamiast zmniejszyć głośność, wywołaj funkcję setWillPauseWhenDucked(true) , utwórz i ustaw OnAudioFocusChangeListener zgodnie z opisem w sekcji automatyczne wyciszanie.
|
setAcceptsDelayedFocusGain()
|
Żądanie dotyczące aktywności audio może się nie powieść, gdy zaznaczenie zostanie zablokowane przez inną aplikację. Ta metoda włącza opóźnione wzmocnienie fokusu, czyli możliwość asynchronicznego pozyskiwania fokusu, gdy stanie się dostępna.
Pamiętaj, że opóźnione wzmocnienie ostrości działa tylko wtedy, gdy w żądaniu audio podasz też |
setOnAudioFocusChangeListener()
|
Element OnAudioFocusChangeListener jest wymagany tylko wtedy, gdy w żądaniu podasz też willPauseWhenDucked(true) lub setAcceptsDelayedFocusGain(true) .
Są 2 metody ustawiania detektora: jedną z argumentem modułu obsługi i jedną bez niego. Moduł obsługi to wątek, w którym działa detektor. Jeśli nie określisz modułu obsługi, używany będzie moduł powiązany z głównym elementem |
Poniższy przykład pokazuje, jak za pomocą komponentu AudioFocusRequest.Builder
utworzyć AudioFocusRequest
oraz wysłać żądanie i porzucić aktywność audio:
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
Na Androidzie 8.0 (poziom interfejsu API 26), gdy inna aplikacja prosi o koncentrację za pomocą funkcji AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
, system może wyciszyć i przywrócić głośność bez wywoływania wywołania zwrotnego onAudioFocusChange()
aplikacji.
Automatyczne wyciszanie jest dopuszczalne w aplikacjach do odtwarzania muzyki i filmów, ale nie jest przydatne podczas odtwarzania treści głosowych, np. w aplikacji audiobooków. W takim przypadku aplikacja powinna zostać wstrzymana.
Jeśli chcesz, aby aplikacja wstrzymywała się, gdy pojawi się prośba o wyciszenie, a nie zmniejszanie głośności, utwórz OnAudioFocusChangeListener
z metodą wywołania zwrotnego onAudioFocusChange()
, która zapewnia oczekiwane wstrzymywanie i wznawianie działania.
Wywołaj metodę setOnAudioFocusChangeListener()
, aby zarejestrować detektor, i wywołaj metodę setWillPauseWhenDucked(true)
, aby nakazać systemowi użycie wywołania zwrotnego zamiast automatycznego wyciszania.
Opóźnione wzmocnienie ostrości
Czasami system nie może spełnić żądania skupienia się na dźwięku, ponieważ zaznaczenie zostało zablokowane przez inną aplikację, na przykład podczas rozmowy telefonicznej. W tym przypadku requestAudioFocus()
zwraca wartość AUDIOFOCUS_REQUEST_FAILED
. W takiej sytuacji aplikacja nie powinna kontynuować odtwarzania dźwięku, ponieważ nie została w niej zwiększona.
Metoda setAcceptsDelayedFocusGain(true)
, która umożliwia aplikacji asynchroniczną obsługę żądania skupienia. Po ustawieniu tej flagi żądanie wysłane, gdy zaznaczenie jest zablokowane, zwraca wartość AUDIOFOCUS_REQUEST_DELAYED
. Gdy warunek blokowania dźwięku już nie istnieje, np. po zakończeniu połączenia telefonicznego, system zaakceptuje oczekujące żądanie zaznaczenia i wywołuje wtedy onAudioFocusChange()
, aby powiadomić aplikację.
Aby radzić sobie z opóźnionym wzmocnieniem, musisz utworzyć OnAudioFocusChangeListener
z metodą wywołania zwrotnego onAudioFocusChange()
, która implementuje pożądane działanie i rejestruje detektor przez wywołanie setOnAudioFocusChangeListener()
.
Dźwięk w Androidzie 7.1 i starszych
Wywołując metodę requestAudioFocus()
, musisz podać wskazówkę dotyczącą czasu trwania, która może być uznawana przez inną aplikację, która aktualnie trwa włączanie i wyłączanie:
- Poproś o stałe sterowanie dźwiękiem (
AUDIOFOCUS_GAIN
), gdy planujesz odtwarzać dźwięk w najbliższej przyszłości (np. podczas odtwarzania muzyki) i spodziewasz się, że poprzednia aktywność audio przestanie się odtwarzać. - Wyślij prośbę o przejściowy ostrość (
AUDIOFOCUS_GAIN_TRANSIENT
), jeśli spodziewasz się, że dźwięk będzie odtwarzany jedynie przez krótki czas, a poprzednia karta wstrzyma odtwarzanie. - Poproś o przejściową koncentrację za pomocą funkcji wyciszania (
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
), aby wskazać, że spodziewasz się, że dźwięk będzie odtwarzany przez krótki czas, oraz że poprzedni właściciel fokusu może kontynuować odtwarzanie, jeśli „wyciszy” (zmniejsza) wyjście audio. Oba wyjścia audio są mieszane w strumieniu audio. Funkcja wyciszania jest szczególnie przydatna w przypadku aplikacji, które nieregularnie korzystają ze strumienia audio, np. w przypadku z włączonym dźwiękiem.
Metoda requestAudioFocus()
wymaga też klasy AudioManager.OnAudioFocusChangeListener
. Detektor należy utworzyć w tej samej aktywności lub usłudze, do której należy sesja multimediów. Implementuje ona wywołanie zwrotne onAudioFocusChange()
, które aplikacja odbiera, gdy jakaś inna aplikacja przejmuje lub porzuci aktywność audio.
Ten fragment kodu prosi o na stałe skoncentrowanie się na dźwięku w strumieniu STREAM_MUSIC
i rejestruje OnAudioFocusChangeListener
w celu obsługi kolejnych zmian fokusu audio. (Funkcja detektora zmian jest omówiona w artykule Reagowanie na zmianę skupienia 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 wywołaj abandonAudioFocus()
.
Kotlin
audioManager.abandonAudioFocus(afChangeListener)
Java
// Abandon audio focus when playback complete audioManager.abandonAudioFocus(afChangeListener);
Spowoduje to wysłanie powiadomienia do systemu, że nie musisz już wybierać, i wyrejestrowanie powiązanego zasobu OnAudioFocusChangeListener
. Jeśli poprosisz o tymczasowe sterowanie, aplikacja, która została wstrzymana lub wyciszona, otrzyma powiadomienie, że może kontynuować odtwarzanie lub przywrócić głośność.
Reakcja na zmianę skupienia dźwięku
Gdy aplikacja skupia się na dźwięku, musi być w stanie ją zwolnić, gdy inna aplikacja zażąda samej aktywności audio. W takim przypadku aplikacja otrzyma wywołanie metody onAudioFocusChange()
w elemencie AudioFocusChangeListener
podanym przez Ciebie podczas wywoływania metody requestAudioFocus()
.
Parametr focusChange
przekazany do onAudioFocusChange()
wskazuje rodzaj zachodzącej zmiany. Odpowiada ona wskazówce dotyczącej czasu trwania wykorzystywanej przez aplikację, która przyciąga uwagę. Aplikacja powinna
odpowiednio reagować.
- Przejściowa utrata koncentracji
- Jeśli zmiana zaznaczenia jest przejściowa (
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
lubAUDIOFOCUS_LOSS_TRANSIENT
), aplikacja powinna się uciszyć (jeśli nie korzystasz z automatycznego wyciszania tła) lub wstrzymać odtwarzanie, ale w przeciwnym razie utrzymać ten sam stan.Gdy chwilowa utrata ostrości dźwięku jest niska, sprawdzaj dalej zmiany ostrości dźwięku i przygotuj się na wznowienie normalnego odtwarzania, gdy odzyskasz ostrość. Gdy aplikacja blokująca zrezygnuje z zaznaczenia, otrzymasz wywołanie zwrotne (
AUDIOFOCUS_GAIN
). W tym momencie możesz przywrócić normalny poziom głośności lub ponownie rozpocząć odtwarzanie. - Trwała utrata koncentracji
- Jeśli utrata aktywności dźwięku jest trwała (
AUDIOFOCUS_LOSS
), oznacza to, że inna aplikacja odtwarza dźwięk. Aplikacja powinna natychmiast wstrzymać odtwarzanie, ponieważ nie otrzyma nigdy wywołania zwrotnegoAUDIOFOCUS_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 komponentu Handler
do opóźnienia zatrzymania wywołania zwrotnego w przypadku trwałej utraty fokusu.
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 } } };
Moduł obsługi używa elementu Runnable
, który 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 opóźnione zatrzymanie nie uruchomiło się, gdy użytkownik ponownie uruchomi odtwarzanie, w odpowiedzi na każdą zmianę stanu wywołaj mHandler.removeCallbacks(mDelayedStopRunnable)
. Wywołaj na przykład removeCallbacks()
w funkcji onPlay()
wywołania zwrotnego, onSkipToNext()
itp. Należy też wywołać tę metodę w wywołaniu zwrotnym onDestroy()
usługi podczas czyszczenia zasobów używanych przez usługę.