Dois ou mais apps Android podem tocar áudio no mesmo stream de saída simultaneamente, e o sistema mistura tudo. Embora isso seja impressionante tecnicamente, pode ser muito desagradável para o usuário. Para evitar todos de música sendo reproduzido ao mesmo tempo, o Android introduz a ideia de áudio em foco. Somente um app pode manter a seleção de áudio por vez.
Quando seu app precisa da saída de áudio, é necessário solicitar a seleção de áudio. Quando pode tocar sons. No entanto, depois de adquirir a seleção de áudio, talvez você não seja poderá mantê-lo até terminar de jogar. Outro app pode solicitar foco, interrompe a retenção da seleção de áudio. Se isso acontecer, seu app será pausado reproduzir ou diminuir o volume para que os usuários ouçam a nova fonte de áudio com mais facilidade.
Antes do Android 12 (nível 31 da API), a seleção de áudio não era gerenciada pelo sistema. Então, enquanto os desenvolvedores de apps são incentivados a seguir as diretrizes de seleção de áudio, Se um app continuar tocando em volume alto, mesmo depois de perder a seleção de áudio em um dispositivo executando o Android 11 (nível 30 da API) ou versões anteriores, o sistema não pode impedir o uso desse recurso. No entanto, esse comportamento do aplicativo causa uma má experiência do usuário e pode levar que os usuários desinstalem o aplicativo com comportamento inadequado.
Um app de áudio bem projetado precisa gerenciar a seleção de áudio de acordo com essas diretrizes diretrizes:
Chame
requestAudioFocus()
imediatamente antes de começar a tocar e verifique se a chamada retornaAUDIOFOCUS_REQUEST_GRANTED
. Faça a chamada pararequestAudioFocus()
no callbackonPlay()
da sessão de mídia.Quando outro app receber a seleção de áudio, pare ou pause a reprodução ou diminua o volume (ou seja, reduzir) o volume.
Quando a reprodução for interrompida (por exemplo, quando o app não tiver mais nada para reproduzir), abandonar a seleção de áudio. Seu app não precisará abandonar a seleção de áudio se o usuário pausa a reprodução, mas pode retomá-la mais tarde.
Use
AudioAttributes
para descrever o tipo de áudio que o app está tocando. Por exemplo, no caso de apps que reproduzem fala, especificarCONTENT_TYPE_SPEECH
A seleção de áudio é tratada de forma diferente dependendo da versão do Android que está em execução:
- Android 12 (nível 31 da API) ou mais recente
- A seleção de áudio é gerenciada pelo sistema. O sistema força a reprodução de áudio a partir de uma esmaeça quando outro app solicita a seleção de áudio. O sistema também silencia a reprodução do áudio quando uma ligação recebida é recebida.
- Do Android 8.0 (nível 26 da API) ao Android 11 (nível 30 da API)
- A seleção de áudio não é gerenciada pelo sistema, mas inclui algumas alterações que foram Introduzido no Android 8.0 (nível 26 da API) e versões mais recentes.
- Android 7.1 (nível 25 da API) e versões anteriores
- A seleção de áudio não é gerenciada pelo sistema, e os apps gerenciam a seleção de áudio usando
requestAudioFocus()
eabandonAudioFocus()
Seleção de áudio no Android 12 e mais recentes
Um app de mídia ou jogo que usa a seleção de áudio não precisa reproduzir áudio depois de perder o foco. No Android 12 (nível 31 da API) e versões mais recentes, o sistema aplica isso do seu modelo. Quando um app solicita a seleção de áudio enquanto outro app está em foco e está sendo reproduzido, o sistema força o app a esmaecer. A adição do elemento o esmaecimento proporciona uma transição mais suave ao passar de um app para outro.
Esse comportamento de esmaecimento acontece quando as condições a seguir são atendidas:
O primeiro app em execução atende a todos estes critérios:
- O app tem
AudioAttributes.USAGE_MEDIA
ou Atributo de uso daAudioAttributes.USAGE_GAME
. - O app solicitou a seleção de áudio com
AudioManager.AUDIOFOCUS_GAIN
. - O app não está tocando áudio com o tipo de conteúdo
AudioAttributes.CONTENT_TYPE_SPEECH
.
- O app tem
Um segundo app solicita a seleção de áudio com
AudioManager.AUDIOFOCUS_GAIN
.
Quando essas condições são atendidas, o sistema de áudio esmaece o primeiro app. No fim do esmaecimento, o sistema notifica o primeiro app sobre a perda de foco. O Os players permanecem silenciados até que o app solicite a seleção de áudio novamente.
Comportamentos de seleção de áudio existentes
Você também precisa conhecer esses outros casos que envolvem um interruptor na seleção de áudio.
Redução automática de volume
Redução temporária do nível de áudio de um app para que outra pode ser ouvida claramente) foi introduzida no Android 8.0 (API de nível 26).
Ao fazer com que o sistema implemente a redução de volume, você não precisa implementar a redução seu app.
A redução automática também ocorre quando uma notificação de áudio recebe o foco no lugar de um app em reprodução. O início da reprodução da notificação é sincronizado com o final da rampa de redução.
A redução automática ocorre quando as seguintes condições são atendidas:
O primeiro app em execução no momento atende a todos estes critérios:
- O app solicitou a seleção de áudio com qualquer tipo de foco ganhar.
- O app não está tocando áudio com o tipo de conteúdo
AudioAttributes.CONTENT_TYPE_SPEECH
: - O app não foi definido
AudioFocusRequest.Builder.setWillPauseWhenDucked(true)
Um segundo app solicita a seleção de áudio com
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
Quando essas condições são atendidas, o sistema de áudio abaixará todos os participantes ativos do para o primeiro app, enquanto o segundo tem o foco. Quando o segundo app é abandonado o foco é imperdível. O primeiro app não é notificado quando perde o foco. então ele não precisa fazer nada.
A redução automática de volume não é realizada quando o usuário está ouvindo conteúdo de fala, porque o usuário pode perder parte do programa. Por exemplo: a orientação por voz das rotas de carro não diminui.
Silenciar a reprodução de áudio atual para chamadas telefônicas recebidas
Alguns apps não se comportam corretamente e continuam a tocar áudio durante chamadas telefônicas. Esta situação força o usuário a encontrar e silenciar ou sair do aplicativo ofensivo em para ouvir a chamada. Para evitar isso, o sistema pode desativar o áudio de outros enquanto recebe uma chamada. O sistema invoca esse recurso quando uma uma chamada telefônica recebida é recebida e um aplicativo atende a estas condições:
- O app tem a propriedade
AudioAttributes.USAGE_MEDIA
ouAudioAttributes.USAGE_GAME
. - O app solicitou a seleção de áudio (qualquer ganho de foco) e está tocando. áudio.
Se um app continuar sendo reproduzido durante a chamada, a reprodução dele será silenciada até a chamada ser encerrada. No entanto, se um aplicativo começar a ser reproduzido durante a chamada, esse player não será silenciado, supondo que o usuário iniciou a reprodução intencionalmente.
Seleção de áudio no Android 8.0 ao 11
A partir do Android 8.0 (nível 26 da API), ao chamar
requestAudioFocus()
é preciso fornecer um parâmetro AudioFocusRequest
. O AudioFocusRequest
contém informações sobre o contexto e os recursos de áudio do seu app. A
sistema usa essas informações para gerenciar o ganho e a perda da seleção de áudio
automaticamente. Para liberar a seleção de áudio, chame o método
abandonAudioFocusRequest()
que também usa um AudioFocusRequest
como argumento. Use a mesma
Instância de AudioFocusRequest
quando você solicita e abandona o foco.
Para criar uma AudioFocusRequest
, use uma
AudioFocusRequest.Builder
Como uma solicitação de foco precisa
sempre especificar o tipo da solicitação, o tipo é incluído no construtor
para o builder. Use os métodos do builder para definir os outros campos do
solicitação.
O campo FocusGain
é obrigatório. Todos os outros são opcionais.
Método | Observações |
---|---|
setFocusGain()
|
Este campo é obrigatório em todas as solicitações. Ele usa os mesmos valores
o durationHint usado na chamada para requestAudioFocus() antes do Android 8.0:
AUDIOFOCUS_GAIN , AUDIOFOCUS_GAIN_TRANSIENT ,
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ou AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE .
|
setAudioAttributes()
|
AudioAttributes descreve o caso de uso do seu app. A
sistema olha para eles quando um aplicativo ganha e perde a seleção de áudio. Atributos
substitui a noção de tipo de fluxo. No Android 8.0 (nível 26 da API) e versões mais recentes,
Os tipos de stream para qualquer operação que não seja os controles de volume foram descontinuados. Usar
os mesmos atributos na solicitação de seleção que você usa em seu player de áudio (como
como mostrado no exemplo após esta tabela).
Use um
Se não for especificado, |
setWillPauseWhenDucked()
|
Quando outro app pede foco com
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK , o app em foco não
geralmente recebem uma
onAudioFocusChange()
porque o sistema pode fazer o
a reduzir o volume. Quando você precisar pausar a reprodução em vez
do que reduzir o volume, chame setWillPauseWhenDucked(true) , crie e defina um
OnAudioFocusChangeListener , conforme descrito no artigo automático
redução de volume.
|
setAcceptsDelayedFocusGain()
|
Uma solicitação de seleção de áudio pode falhar quando ela é bloqueada por outro app.
Esse método ativa o ganho atrasado da seleção: a capacidade
para adquirir o foco de forma assíncrona quando estiver disponível.
O ganho atrasado da seleção só funciona se você também especificar um
|
setOnAudioFocusChangeListener()
|
Um OnAudioFocusChangeListener só será necessário se você também especificar
willPauseWhenDucked(true) ou setAcceptsDelayedFocusGain(true) na solicitação.
Há dois métodos para definir o listener: um com e outro sem
manipulador. O gerenciador é a sequência em que o listener é executado. Se você
não especifique um manipulador, o gerenciador associado à interface principal
|
O exemplo abaixo mostra como usar um AudioFocusRequest.Builder
para criar
um AudioFocusRequest
para solicitar e abandonar a seleção de áudio:
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; } } }
Redução automática de volume
No Android 8.0 (API de nível 26), quando outro aplicativo solicita foco com
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
: o sistema pode reduzir e restaurar o volume.
sem invocar o callback onAudioFocusChange()
do app.
Embora a redução automática de volume seja um comportamento aceitável para a reprodução de músicas e vídeos, aplicativos, não é útil ao reproduzir conteúdo falado, como em um aplicativo de audiolivros. Nesse caso, o app precisa ser pausado.
Se você quiser que o app seja pausado quando solicitado a reduzir o volume em vez de diminuir o volume, crie uma OnAudioFocusChangeListener
com
um método de callback onAudioFocusChange()
que implementa o comportamento de pausa/retomada desejado
Chame setOnAudioFocusChangeListener()
para registrar o listener e chame
setWillPauseWhenDucked(true)
para instruir o sistema a usar o callback em vez de realizar a redução automática de volume.
Ganho atrasado de seleção
Às vezes, o sistema não pode conceder uma solicitação de seleção de áudio porque ela está
"bloqueado" por outro app, como durante uma chamada telefônica. Nesse caso,
requestAudioFocus()
retorna AUDIOFOCUS_REQUEST_FAILED
. Quando isso acontece,
seu app não deve continuar com a reprodução de áudio porque não ganhou
foco.
O método setAcceptsDelayedFocusGain(true)
, que permite que o app processe uma solicitação de foco.
de forma assíncrona. Com esse flag definido, uma solicitação feita quando o foco está bloqueado
retorna AUDIOFOCUS_REQUEST_DELAYED
. Quando a condição que bloqueou o áudio
não existe mais, por exemplo, quando uma ligação é encerrada, o sistema
concede a solicitação de foco pendente e chama onAudioFocusChange()
para notificar seu
app.
Para lidar com o ganho atrasado de foco, é necessário criar um
OnAudioFocusChangeListener
por um método de callback onAudioFocusChange()
que
implementa o comportamento desejado e registra o listener chamando
setOnAudioFocusChangeListener()
.
Seleção de áudio no Android 7.1 e versões anteriores
Quando você ligar
requestAudioFocus()
você deve especificar uma dica de duração, que pode
ser honrado por outro aplicativo que está mantendo o foco e tocando no momento:
- Solicitar seleção de áudio permanente (
AUDIOFOCUS_GAIN
) quando planeja tocar áudio em um futuro próximo (por exemplo, ao tocar música) e espera que detentor anterior de seleção de áudio para interromper a reprodução. - Solicitar o foco temporário (
AUDIOFOCUS_GAIN_TRANSIENT
) quando esperar a reprodução o áudio por um curto período e você espera que o detentor anterior pause a reprodução. - Solicitar foco temporário com redução de volume
(
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
) para indicar que você espera tocar áudio por um curto período e que não há problema para o proprietário do foco anterior manter tocar se "patos" (diminui) a saída de áudio. As duas saídas de áudio são misturadas no stream de áudio. A redução de volume é particularmente adequada para apps que usam o o stream de áudio de maneira intermitente, por exemplo, para rotas de carro audíveis.
O método requestAudioFocus()
também requer um AudioManager.OnAudioFocusChangeListener
. Esse listener precisa ser
criados na mesma atividade ou serviço que é proprietário da sua sessão de mídia. Ela
implementa o callback onAudioFocusChange()
que o app recebe quando
Algum outro app adquire ou abandona a seleção de áudio.
O snippet a seguir solicita a seleção de áudio permanente no stream
STREAM_MUSIC
e registra um OnAudioFocusChangeListener
para processar
mudanças subsequentes na seleção de áudio. O listener de mudanças é discutido em
Como responder a uma mudança de seleção de áudio.
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 }
Quando terminar a reprodução, chame
abandonAudioFocus()
Kotlin
audioManager.abandonAudioFocus(afChangeListener)
Java
// Abandon audio focus when playback complete audioManager.abandonAudioFocus(afChangeListener);
Isso notifica o sistema de que você não precisa mais da seleção e cancela o registro do
associado OnAudioFocusChangeListener
. Se você solicitou a seleção temporária,
isso vai notificar um app que pausou ou diminuiu para que ele pode continuar a reprodução ou
restaurar o volume.
Como responder a uma mudança de seleção de áudio
Quando um app adquire a seleção de áudio, ele precisa ser capaz de liberá-la quando outro app
solicita a seleção de áudio para si. Quando isso acontece, seu app
recebe uma chamada
onAudioFocusChange()
na classe AudioFocusChangeListener
que você especificou quando o app chamou requestAudioFocus()
.
O parâmetro focusChange
transmitido para onAudioFocusChange()
indica o tipo
das mudanças que estão acontecendo. Ele corresponde
à dica de duração usada pelo app que está adquirindo foco. Seu app precisa
responder adequadamente.
- Perda transitória de seleção
-
Se a mudança de foco for temporária (
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
ouAUDIOFOCUS_LOSS_TRANSIENT
), o app precisa se retrair (se você não estiver confiando na redução automática de volume) ou pausar a reprodução, mas caso contrário, manteriam o mesmo estado.Durante uma perda transitória da seleção de áudio, continue monitorando as mudanças. na seleção de áudio e esteja preparado para retomar a reprodução normal quando você recuperar foco. Quando o app de bloqueio abandonar o foco, você vai receber um callback (
AUDIOFOCUS_GAIN
). Neste ponto, é possível restaurar o volume para o nível normal ou reiniciar a reprodução. - Perda permanente de seleção
-
Se a perda da seleção de áudio for permanente (
AUDIOFOCUS_LOSS
), outro app será a reprodução de áudio. O app precisa pausar a reprodução imediatamente, como nunca receba um callbackAUDIOFOCUS_GAIN
. Para reiniciar a reprodução, o usuário precisa realizar uma ação explícita, como pressionar o controle de transporte para reprodução; em uma notificação ou na interface do app.
O snippet de código a seguir demonstra como implementar a
OnAudioFocusChangeListener
e o callback onAudioFocusChange()
. Observe que
Uso de um Handler
para atrasar o callback de parada em uma perda permanente de áudio.
foco.
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 } } };
O gerenciador usa um Runnable
que tem esta aparência:
Kotlin
private var delayedStopRunnable = Runnable { mediaController.transportControls.stop() }
Java
private Runnable delayedStopRunnable = new Runnable() { @Override public void run() { getMediaController().getTransportControls().stop(); } };
Para garantir que a parada atrasada não seja iniciada se o usuário reiniciar a reprodução, chame
mHandler.removeCallbacks(mDelayedStopRunnable)
em resposta a qualquer estado
mudanças. Por exemplo, chame removeCallbacks()
no onPlay()
do callback.
onSkipToNext()
etc. Você também deve chamar esse método na classe
onDestroy()
ao limpar os recursos usados pelo serviço.