Zarządzaj aktywnością audio

Co najmniej 2 aplikacje na Androida mogą odtwarzać dźwięk do tego samego strumienia wyjściowego jednocześnie, a system wszystko miksuje. Chociaż jest to imponujące pod względem technicznym, może być bardzo irytujące dla użytkownika. Aby uniknąć jednoczesnego odtwarzania dźwięku przez wszystkie aplikacje muzyczne, Android wprowadza koncepcję skupienia dźwięku. Tylko jedna aplikacja może mieć aktywność audio w danym momencie.

Gdy aplikacja musi odtwarzać dźwięk, powinna poprosić o aktywność audio. Gdy ma fokus, może odtwarzać dźwięk. Po uzyskaniu aktywności audio możesz jednak nie być w stanie utrzymać jej do końca odtwarzania. Inna aplikacja może poprosić o aktywność audio, co spowoduje tymczasowe przerwanie Twojego dostępu do aktywności audio. W takiej sytuacji aplikacja powinna wstrzymać odtwarzanie lub zmniejszyć głośność, aby użytkownicy mogli łatwiej usłyszeć nowe źródło dźwięku.

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

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

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

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

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

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

Aktywność audio jest obsługiwana w różny sposób w zależności od wersji Androida:

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

Aktywność audio w Androidzie 12 i nowszych wersjach

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

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

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

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

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

Dotychczasowe zachowania związane z aktywnością audio

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

Automatyczne wyciszanie

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

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

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

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

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

  2. Druga aplikacja wysyła żądanie aktywności audio z parametrem AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

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

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

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

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

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

Jeśli aplikacja 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 połączenia, nie zostanie wyciszona, ponieważ zakłada się, że użytkownik rozpoczął odtwarzanie celowo.

Aktywność audio w Androidzie w wersji od 8.0 do 11

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

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

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

MetodaUwagi
setFocusGain() To pole jest wymagane w każdej prośbie. Przyjmuje te same wartości co durationHint używane w wywołaniu requestAudioFocus() przed Androidem 8.0:AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT,AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK lub AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
setAudioAttributes() AudioAttributes opisuje przypadek użycia aplikacji. System sprawdza je, gdy aplikacja uzyskuje i traci aktywność audio. Atrybuty zastępują pojęcie typu strumienia. W Androidzie 8.0 (poziom interfejsu API 26) i nowszych wersjach typy strumieni dla wszystkich operacji innych niż sterowanie 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 poniżej tej tabeli).

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

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

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

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

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

Słuchacza można ustawić na 2 sposoby: z argumentem obsługi i bez niego. Obsługa to wątek, w którym działa odbiornik. Jeśli nie określisz modułu obsługi, zostanie użyty moduł obsługi powiązany z głównym Looper.

Ten przykład pokazuje, jak za pomocą AudioFocusRequest.Builder utworzyć AudioFocusRequest oraz wysłać żądanie i zrezygnować z aktywności 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

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

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

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

Opóźnione uzyskanie ostrości

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

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

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

Aktywność audio w Androidzie 7.1 i starszych wersjach

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

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

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

Poniższy fragment kodu wysyła prośbę o stałą aktywność audio na strumieniu STREAM_MUSIC i rejestruje OnAudioFocusChangeListener do obsługi kolejnych zmian w aktywności audio. (Detektor zmian został omówiony w sekcji Reagowanie na zmianę aktywności audio).

Kotlin

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

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

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

Java

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

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

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

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

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

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

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

Reagowanie na zmianę aktywności audio

Gdy aplikacja uzyska aktywność audio, musi być w stanie ją zwolnić, gdy inna aplikacja poprosi o aktywność audio dla siebie. W takiej sytuacji aplikacja otrzymuje wywołanie metody onAudioFocusChange() w obiekcie AudioFocusChangeListener, który został określony, gdy aplikacja wywołała metodę requestAudioFocus().

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

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

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

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

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

Kotlin

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

Java

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

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

Kotlin

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

Java

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

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