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 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 zwraca AUDIOFOCUS_REQUEST_GRANTED. Wykonaj wywołanie requestAudioFocus() w wywołaniu zwrotnym onPlay() 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() i abandonAudioFocus().

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:

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

  2. 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:

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

  2. 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 lub AudioAttributes.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.

MetodaUwagi
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 AudioAttributes.Builder, aby najpierw określić atrybuty, a potem użyj tej metody, aby przypisać atrybuty do żądania.

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

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ż AudioManager.OnAudioFocusChangeListener, ponieważ aplikacja musi odebrać wywołanie zwrotne, aby wiedzieć, że zaznaczenie zostało przyznane.

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 Looper.

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 lub AUDIOFOCUS_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 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 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ę.