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 wszystkie dźwięki. Choć jest to imponujące pod względem technicznym, może być bardzo irytujące dla użytkownika. Aby uniknąć sytuacji, w której wszystkie aplikacje muzyczne odtwarzają dźwięk jednocześnie, Android wprowadza koncepcję fokusa audio. Tylko jedna aplikacja może mieć aktywny dźwięk.

Gdy aplikacja musi odtworzyć dźwięk, powinna poprosić o fokus audio. Gdy jest aktywna, może odtwarzać dźwięk. Jednak po uzyskaniu fokusu audio możesz nie być w stanie go utrzymać do końca odtwarzania. Inna aplikacja może poprosić o fokus, co spowoduje przejęcie fokusu audio. 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.

Przed Androidem 12 (poziom API 31) system nie zarządzał fokusem dźwięku. Dlatego, chociaż zachęcamy deweloperów aplikacji do przestrzegania wytycznych dotyczących fokusu audio, jeśli aplikacja nadal odtwarza głośno dźwięk nawet po utracie fokusu audio na urządzeniu z Androidem 11 (API na poziomie 30) lub starszym, system nie może temu zapobiec. Takie działanie aplikacji pogarsza jednak wrażenia użytkowników i często prowadzi do odinstalowania aplikacji, która nie działa prawidłowo.

Dobrze zaprojektowana aplikacja audio powinna zarządzać fokusem audio zgodnie z tymi ogólnymi wytycznymi:

  • Wywołaj requestAudioFocus() bezpośrednio przed rozpoczęciem odtwarzania i sprawdź, czy wywołanie zwraca wartość AUDIOFOCUS_REQUEST_GRANTED. Wywołaj requestAudioFocus() w wywołaniu zwrotnym onPlay() sesji multimedialnej.

  • Gdy inna aplikacja uzyska fokus audio, zatrzymaj lub wstrzymaj odtwarzanie albo zmniejsz głośność.

  • Gdy odtwarzanie się zatrzyma (np. gdy aplikacja nie ma już nic do odtworzenia), porzuć fokus audio. 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ć typ dźwięku odtwarzanego przez aplikację. Na przykład w przypadku aplikacji odtwarzających mowę podaj CONTENT_TYPE_SPEECH.

Zarządzanie fokusem dźwięku różni się w zależności od wersji Androida:

thought Android 12 (poziom interfejsu API 31) lub nowszy
Fokus audio jest zarządzany przez system. System wymusza wyciszenie odtwarzania dźwięku z aplikacji, gdy inna aplikacja żąda skupienia na dźwięku. System wycisza też odtwarzanie dźwięku, gdy nadejdzie połączenie przychodzące.
Android 8.0 (poziom API 26) do Androida 11 (poziom API 30)
Fokus audio nie jest zarządzany przez system, ale obejmuje pewne zmiany wprowadzone od Androida 8.0 (poziom API 26).
Android 7.1 (poziom interfejsu API 25) lub starszy
Fokus audio nie jest zarządzany przez system, a aplikacje zarządzają fokusem audio za pomocą requestAudioFocus() i abandonAudioFocus().

Zarządzanie dźwiękiem w Androidzie 12 i nowszych wersjach

Aplikacja multimedialna lub gra mobilna, która korzysta z aktywności audio, nie powinna odtwarzać dźwięku po jej utracie. W Androidzie 12 (API na poziomie 31) i nowszych system wymusza to zachowanie. Gdy aplikacja prosi o fokus audio, podczas gdy inna aplikacja ma fokus i odtwarza dźwięk, system wymusza wyciszenie odtwarzającej aplikacji. Dodanie efektu zanikania 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 skupienie na dźwięku za pomocą AudioManager.AUDIOFOCUS_GAIN.

Gdy te warunki zostaną spełnione, system audio wyciszy pierwszą aplikację. Po zakończeniu wyciszania system powiadomi pierwszą aplikację o utracie fokusu. Odtwarzacze aplikacji pozostają wyciszone, dopóki aplikacja nie poprosi ponownie o fokus audio.

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

Pamiętaj też o innych przypadkach, w których następuje przełączenie fokusu audio.

Automatyczne wyciszanie

Automatyczne wyciszanie tła (tymczasowe zmniejszanie poziomu dźwięku jednej aplikacji, aby można było 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 implementować go w aplikacji.

Automatyczne wyciszanie występuje też wtedy, gdy powiadomienie dźwiękowe przejmuje fokus z odtwarzającej aplikacji. Rozpoczęcie odtwarzania powiadomienia jest zsynchronizowane z końcem wyciszania.

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

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

  2. Druga aplikacja prosi o fokus audio za pomocą AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Gdy te warunki są spełnione, system audio wycisza wszystkie aktywne odtwarzacze pierwszej aplikacji, gdy druga aplikacja jest aktywna. Gdy druga aplikacja przestanie być aktywna, głośność zostanie przywrócona. Pierwsza aplikacja nie jest powiadamiana o utracie fokusu, 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ą wyciszane.

Wyciszanie odtwarzanego dźwięku w przypadku połączeń przychodzących

Niektóre aplikacje nie działają prawidłowo i nadal odtwarzają dźwięk podczas połączeń telefonicznych. W takiej sytuacji użytkownik musi znaleźć i wyciszyć lub zamknąć aplikację, która powoduje problem, aby usłyszeć rozmówcę. Aby temu zapobiec, system może wyciszyć dźwięk z innych aplikacji, gdy nadejdzie połączenie przychodzące. System wywołuje tę funkcję, gdy zostanie odebrane połączenie telefoniczne, a aplikacja spełnia te warunki:

  • Aplikacja ma atrybut wykorzystania AudioAttributes.USAGE_MEDIA lub AudioAttributes.USAGE_GAME.
  • Aplikacja pomyślnie poprosiła o fokus audio (dowolny rodzaj fokusu) i odtwarza dźwięk.

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

Zarządzanie dźwiękiem w Androidzie 8.0–11

Od Androida 8.0 (poziom API 26) podczas wywoływania requestAudioFocus() musisz podać parametr AudioFocusRequest. AudioFocusRequest zawiera informacje o kontekście i możliwościach audio aplikacji. System używa tych informacji do automatycznego zarządzania uzyskiwaniem i utratą fokusu audio. Aby zwolnić zaznaczenie dźwięku, wywołaj metodę abandonAudioFocusRequest(), która również przyjmuje argument AudioFocusRequest. Używaj tej samej instancji AudioFocusRequest zarówno podczas żądania, jak i porzucania fokusu.

Aby utworzyć AudioFocusRequest, użyj AudioFocusRequest.Builder. Żądanie skupienia uwagi musi zawsze określać typ żądania, dlatego typ jest uwzględniony w konstruktorze kreatora. Użyj metod kreatora, aby ustawić inne 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żywany w wywołaniu requestAudioFocus() w systemie Android w wersji starszej niż 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ż regulacja głośności są wycofane. W żądaniu fokusu używaj tych samych atrybutów, których używasz w odtwarzaczu audio (jak pokazano w przykładzie pod tą tabelą).

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

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

setWillPauseWhenDucked() Gdy inna aplikacja żąda fokusu za pomocą AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, aplikacja, która ma fokus, zwykle nie otrzymuje wywołania zwrotnego onAudioFocusChange(), ponieważ system może samodzielnie wyciszyć dźwięk. Jeśli chcesz wstrzymać odtwarzanie, a nie wyciszyć dźwięk, wywołaj setWillPauseWhenDucked(true) oraz utwórz i ustaw OnAudioFocusChangeListener, jak opisano w sekcji Automatyczne wyciszanie.
setAcceptsDelayedFocusGain() Prośba o fokus audio może się nie powieść, jeśli fokus jest zablokowany przez inną aplikację. Ta metoda umożliwia opóźnione uzyskanie fokusu, czyli asynchroniczne uzyskanie fokusu, gdy stanie się on dostępny.

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

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

Istnieją 2 metody ustawiania detektora: jedna z argumentem modułu obsługi i jedna bez niego. Moduł obsługi to wątek, w którym działa detektor. Jeśli nie określisz modułu obsługi, zostanie użyty moduł obsługi powiązany z głównym Looper.

Ten przykład pokazuje, jak użyć AudioFocusRequest.Builder do utworzenia AudioFocusRequest oraz poprosić o fokus audio i go porzucić:

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 żąda fokusu za pomocą 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 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 audiobooków. W takim przypadku aplikacja powinna wstrzymać odtwarzanie.

Jeśli chcesz, aby aplikacja wstrzymywała odtwarzanie, gdy zostanie poproszona o wyciszenie, zamiast zmniejszać głośność, utwórz OnAudioFocusChangeListener z metodą wywołania zwrotnego onAudioFocusChange(), która implementuje odpowiednie zachowanie wstrzymywania i wznawiania. Wywołaj setOnAudioFocusChangeListener(), aby zarejestrować detektor, i wywołaj setWillPauseWhenDucked(true), aby poinformować system, że ma używać wywołania zwrotnego zamiast automatycznego wyciszania.

Opóźnione uzyskanie ostrości

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

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

Aby obsłużyć opóźnione uzyskanie fokusu, musisz utworzyć obiekt OnAudioFocusChangeListener z metodą wywołania zwrotnego onAudioFocusChange(), która implementuje odpowiednie działanie, i zarejestrować detektor, wywołując metodę setOnAudioFocusChangeListener().

Fokus audio w Androidzie 7.1 i starszych wersjach

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

  • Poproś o stałe skupienie dźwięku (AUDIOFOCUS_GAIN), gdy planujesz odtwarzać dźwięk przez dłuższy czas (np. podczas odtwarzania muzyki) i oczekujesz, że poprzedni posiadacz skupienia dźwięku przestanie odtwarzać.
  • Poproś o tymczasowe skupienie (AUDIOFOCUS_GAIN_TRANSIENT), gdy spodziewasz się, że dźwięk będzie odtwarzany tylko przez krótki czas, a poprzedni właściciel wstrzyma odtwarzanie.
  • Poproś o tymczasowe skupienie uwagi z wyciszeniem (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK), aby wskazać, że chcesz odtwarzać dźwięk tylko przez krótki czas i że poprzedni właściciel skupienia uwagi może kontynuować odtwarzanie, jeśli „wyciszy” (obniży) głośność dźwięku. Oba wyjścia audio są miksowane w strumieniu audio. Wyciszanie jest szczególnie przydatne w przypadku aplikacji, które korzystają ze strumienia audio w sposób przerywany, np. do odtwarzania wskazówek dojazdu.

Metoda requestAudioFocus() wymaga też parametru AudioManager.OnAudioFocusChangeListener. Ten detektor powinien zostać 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 uzyskuje lub porzuca fokus audio.

Poniższy fragment kodu żąda stałego fokusu audio w strumieniu STREAM_MUSIC i rejestruje OnAudioFocusChangeListener do obsługi kolejnych zmian fokusu audio. (Detektor zmian jest omówiony 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 wywołaj metodę abandonAudioFocus().

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

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

Powiadamia to system, że nie potrzebujesz już zaznaczenia, i wyrejestrowuje powiązany element OnAudioFocusChangeListener. Jeśli poprosisz o tymczasowe skupienie, aplikacja, która została wstrzymana lub wyciszona, zostanie powiadomiona, ż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. Gdy tak się stanie, aplikacja otrzyma wywołanie metody onAudioFocusChange() w obiekcie AudioFocusChangeListener, który został określony, gdy aplikacja wywołała requestAudioFocus().

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

Przejściowa utrata ostrości
Jeśli zmiana fokusu 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 zachować ten sam stan.

Podczas przejściowej utraty fokusu audio należy nadal monitorować zmiany w fokusie audio i być przygotowanym na wznowienie normalnego odtwarzania po odzyskaniu fokusu. Gdy aplikacja blokująca zrezygnuje z fokusu, otrzymasz wywołanie zwrotne (AUDIOFOCUS_GAIN). W tym momencie możesz przywrócić normalny poziom głośności lub wznowić odtwarzanie.

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

Poniższy fragment kodu pokazuje, jak wdrożyć OnAudioFocusChangeListener i 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
      }
    }
  };

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 mieć pewność, że opóźnione zatrzymanie nie zostanie uruchomione, jeśli użytkownik ponownie rozpocznie odtwarzanie, wywołaj metodę mHandler.removeCallbacks(mDelayedStopRunnable) w odpowiedzi na wszelkie zmiany stanu. Na przykład wywołaj removeCallbacks() w wywołaniu zwrotnym onPlay(), onSkipToNext() itd. Tę metodę należy też wywołać w wywołaniu zwrotnym onDestroy() usługi podczas czyszczenia zasobów używanych przez usługę.