Deux applications Android ou plus peuvent lire de l'audio sur le même flux de sortie simultanément, et le système mélange le tout. Bien que cela soit techniquement impressionnant, cela peut être très agaçant pour un utilisateur. Pour éviter que toutes les applications de musique ne jouent en même temps, Android introduit le concept de focus audio. Une seule application peut détenir le focus audio à la fois.
Lorsque votre application doit émettre du contenu audio, elle doit demander la priorité audio. Lorsqu'il est au premier plan, il peut émettre du son. Toutefois, une fois que vous avez acquis le focus audio, il est possible que vous ne puissiez pas le conserver jusqu'à la fin de la lecture. Une autre application peut demander la priorité, ce qui interrompt votre priorité audio. Dans ce cas, votre application doit mettre en pause la lecture ou baisser le volume pour permettre aux utilisateurs d'entendre plus facilement la nouvelle source audio.
Avant Android 12 (niveau d'API 31), la priorité audio n'est pas gérée par le système. Par conséquent, même si les développeurs d'applications sont encouragés à respecter les consignes concernant le focus audio, si une application continue de lire du contenu à un volume élevé même après avoir perdu le focus audio sur un appareil équipé d'Android 11 (niveau d'API 30) ou version antérieure, le système ne peut pas l'empêcher. Toutefois, ce comportement d'application nuit à l'expérience utilisateur et peut souvent inciter les utilisateurs à désinstaller l'application en question.
Une application audio bien conçue doit gérer la priorité audio en suivant ces consignes générales :
Appelez
requestAudioFocus()
immédiatement avant de commencer à jouer et vérifiez que l'appel renvoieAUDIOFOCUS_REQUEST_GRANTED
. AppelezrequestAudioFocus()
dans le rappelonPlay()
de votre session multimédia.Lorsqu'une autre application obtient la priorité audio, arrêtez ou mettez en pause la lecture, ou baissez le volume.
Lorsque la lecture s'arrête (par exemple, lorsque l'application n'a plus rien à lire), abandonnez la priorité audio. Votre application n'a pas besoin d'abandonner la priorité audio si l'utilisateur met la lecture en pause, mais pourrait la reprendre plus tard.
Utilisez
AudioAttributes
pour décrire le type d'audio lu par votre application. Par exemple, pour les applications qui lisent des discours, spécifiezCONTENT_TYPE_SPEECH
.
La gestion de la priorité audio diffère selon la version d'Android exécutée :
- Android 12 (niveau d'API 31) ou version ultérieure
- La priorité audio est gérée par le système. Le système force la lecture audio d'une application à s'estomper lorsqu'une autre application demande la priorité audio. Le système coupe également la lecture audio lorsqu'un appel entrant est reçu.
- Android 8.0 (niveau d'API 26) à Android 11 (niveau d'API 30)
- La gestion de la priorité audio n'est pas assurée par le système, mais elle inclut certaines modifications introduites à partir d'Android 8.0 (niveau d'API 26).
- Android 7.1 (niveau d'API 25) ou version antérieure
- La priorité audio n'est pas gérée par le système. Les applications gèrent la priorité audio à l'aide de
requestAudioFocus()
etabandonAudioFocus()
.
Focus audio dans Android 12 et versions ultérieures
Une application multimédia ou de jeu qui utilise la priorité audio ne doit pas lire de contenu audio après avoir perdu la priorité. Dans Android 12 (niveau d'API 31) et versions ultérieures, le système applique ce comportement. Lorsqu'une application demande la priorité audio alors qu'une autre application l'a déjà et est en cours de lecture, le système force l'application en cours de lecture à baisser le volume. L'ajout de l'effet d'atténuation permet une transition plus fluide d'une application à une autre.
Ce comportement de fondu se produit lorsque les conditions suivantes sont remplies :
La première application en cours de lecture répond à tous les critères suivants :
- L'application possède l'attribut d'utilisation
AudioAttributes.USAGE_MEDIA
ouAudioAttributes.USAGE_GAME
. - L'application a demandé la priorité audio avec succès avec
AudioManager.AUDIOFOCUS_GAIN
. - L'application ne lit pas l'audio avec le type de contenu
AudioAttributes.CONTENT_TYPE_SPEECH
.
- L'application possède l'attribut d'utilisation
Une deuxième application demande la priorité audio avec
AudioManager.AUDIOFOCUS_GAIN
.
Lorsque ces conditions sont remplies, le système audio diminue le volume de la première application. À la fin de la diminution du volume, le système notifie à la première application la perte de focus. Les lecteurs de l'application restent en mode silencieux jusqu'à ce que l'application demande à nouveau la priorité audio.
Comportements existants de la priorité audio
Vous devez également tenir compte des autres cas impliquant un changement de focus audio.
Atténuation automatique
La diminution automatique du volume (réduction temporaire du niveau audio d'une application pour qu'une autre puisse être entendue clairement) a été introduite dans Android 8.0 (niveau d'API 26).
En laissant le système implémenter le ducking, vous n'avez pas besoin de l'implémenter dans votre application.
L'atténuation automatique se produit également lorsqu'une notification audio prend le contrôle d'une application en cours de lecture. Le début de la lecture de la notification est synchronisé avec la fin de la rampe d'atténuation.
L'atténuation automatique se produit lorsque les conditions suivantes sont remplies :
La première application en cours de lecture répond à tous les critères suivants :
- L'application a demandé avec succès la priorité audio avec n'importe quel type de gain de priorité.
- L'application ne lit pas l'audio avec le type de contenu
AudioAttributes.CONTENT_TYPE_SPEECH
. - L'application n'a pas défini
AudioFocusRequest.Builder.setWillPauseWhenDucked(true)
.
Une deuxième application demande la priorité audio avec
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
Lorsque ces conditions sont remplies, le système audio réduit le volume de tous les lecteurs actifs de la première application lorsque la seconde est au premier plan. Lorsque la deuxième application perd le focus, elle est désancrée. La première application n'est pas avertie lorsqu'elle perd le focus. Elle n'a donc rien à faire.
Notez que l'atténuation automatique n'est pas effectuée lorsque l'utilisateur écoute du contenu vocal, car il pourrait manquer une partie du programme. Par exemple, le volume du guidage vocal pour les itinéraires en voiture n'est pas baissé.
Couper la lecture audio en cours pour les appels téléphoniques entrants
Certaines applications ne se comportent pas correctement et continuent de lire du contenu audio pendant les appels téléphoniques. Dans ce cas, l'utilisateur est obligé de trouver et de couper le son ou de quitter l'application en cause pour entendre son appel. Pour éviter cela, le système peut couper le son des autres applications en cas d'appel entrant. Le système appelle cette fonctionnalité lorsqu'un appel téléphonique entrant est reçu et qu'une application remplit les conditions suivantes :
- L'application possède l'attribut d'utilisation
AudioAttributes.USAGE_MEDIA
ouAudioAttributes.USAGE_GAME
. - L'application a demandé la priorité audio (tout gain de priorité) et lit l'audio.
Si une application continue de lire du contenu pendant l'appel, sa lecture est mise en sourdine jusqu'à la fin de l'appel. Toutefois, si une application commence à lire du contenu pendant l'appel, le lecteur n'est pas mis en sourdine, car on suppose que l'utilisateur a lancé la lecture intentionnellement.
Focus audio dans Android 8.0 à Android 11
À partir d'Android 8.0 (niveau d'API 26), lorsque vous appelez requestAudioFocus()
, vous devez fournir un paramètre AudioFocusRequest
. Le AudioFocusRequest
contient des informations sur le contexte audio et les capacités de votre application. Le système utilise ces informations pour gérer automatiquement le gain et la perte du focus audio. Pour libérer le focus audio, appelez la méthode abandonAudioFocusRequest()
, qui prend également un AudioFocusRequest
comme argument. Utilisez la même instance AudioFocusRequest
lorsque vous demandez et abandonnez la mise au point.
Pour créer un AudioFocusRequest
, utilisez un AudioFocusRequest.Builder
. Étant donné qu'une demande de focus doit toujours spécifier le type de la demande, le type est inclus dans le constructeur du générateur. Utilisez les méthodes du générateur pour définir les autres champs de la requête.
Le champ FocusGain
est obligatoire, mais tous les autres sont facultatifs.
Méthode | Notes |
---|---|
setFocusGain()
|
Ce champ est obligatoire dans chaque requête. Il prend les mêmes valeurs que durationHint utilisé dans l'appel requestAudioFocus() avant Android 8.0 : AUDIOFOCUS_GAIN , AUDIOFOCUS_GAIN_TRANSIENT , AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ou AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE .
|
setAudioAttributes()
|
AudioAttributes décrit le cas d'utilisation de votre application. Le système les examine lorsqu'une application gagne et perd le focus audio. Les attributs remplacent la notion de type de flux. Dans Android 8.0 (niveau d'API 26) et versions ultérieures, les types de flux pour toute opération autre que les commandes de volume sont obsolètes. Utilisez les mêmes attributs dans la demande de focus que ceux utilisés dans votre lecteur audio (comme indiqué dans l'exemple suivant ce tableau).
Utilisez un
Si aucune valeur n'est spécifiée, |
setWillPauseWhenDucked()
|
Lorsqu'une autre application demande la priorité avec AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK , l'application qui a la priorité ne reçoit généralement pas de rappel onAudioFocusChange() , car le système peut effectuer l'atténuation lui-même. Lorsque vous devez mettre en pause la lecture plutôt que baisser le volume, appelez setWillPauseWhenDucked(true) et créez et définissez un OnAudioFocusChangeListener , comme décrit dans Baisse automatique du volume.
|
setAcceptsDelayedFocusGain()
|
Une demande de priorité audio peut échouer lorsque la priorité est verrouillée par une autre application.
Cette méthode permet d'obtenir la priorité de manière différée : la possibilité
d'acquérir la priorité de manière asynchrone lorsqu'elle devient disponible.
Notez que l'acquisition différée de la mise au point ne fonctionne que si vous spécifiez également un |
setOnAudioFocusChangeListener()
|
Un OnAudioFocusChangeListener n'est requis que si vous spécifiez également willPauseWhenDucked(true) ou setAcceptsDelayedFocusGain(true) dans la requête.
Il existe deux méthodes pour définir l'écouteur : l'une avec un argument de gestionnaire et l'autre sans. Le gestionnaire est le thread sur lequel l'écouteur s'exécute. Si vous ne spécifiez pas de gestionnaire, celui associé au |
L'exemple suivant montre comment utiliser un AudioFocusRequest.Builder
pour créer un AudioFocusRequest
, et demander et abandonner la priorité 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; } } }
Atténuation automatique
Dans Android 8.0 (niveau d'API 26), lorsqu'une autre application demande le focus avec AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
, le système peut baisser et rétablir le volume sans appeler le rappel onAudioFocusChange()
de l'application.
Bien que l'atténuation automatique soit un comportement acceptable pour les applications de lecture de musique et de vidéo, elle n'est pas utile lors de la lecture de contenu parlé, comme dans une application de livre audio. Dans ce cas, l'application doit mettre en pause la lecture.
Si vous souhaitez que votre application se mette en pause lorsqu'elle est invitée à baisser le volume plutôt que de diminuer son volume, créez un OnAudioFocusChangeListener
avec une méthode de rappel onAudioFocusChange()
qui implémente le comportement de pause/reprise souhaité.
Appelez setOnAudioFocusChangeListener()
pour enregistrer l'écouteur et setWillPauseWhenDucked(true)
pour indiquer au système d'utiliser votre rappel au lieu d'effectuer un ducking automatique.
Gain de focus différé
Parfois, le système ne peut pas accorder une demande de priorité audio, car la priorité est "verrouillée" par une autre application, par exemple pendant un appel téléphonique. Dans ce cas, requestAudioFocus()
renvoie AUDIOFOCUS_REQUEST_FAILED
. Dans ce cas, votre application ne doit pas poursuivre la lecture audio, car elle n'a pas été mise au premier plan.
La méthode setAcceptsDelayedFocusGain(true)
permet à votre application de gérer une demande de focus de manière asynchrone. Lorsque cet indicateur est défini, une requête effectuée lorsque la sélection est verrouillée renvoie AUDIOFOCUS_REQUEST_DELAYED
. Lorsque la condition qui a bloqué la priorité audio n'existe plus (par exemple, lorsqu'un appel téléphonique se termine), le système accorde la demande de priorité en attente et appelle onAudioFocusChange()
pour en informer votre application.
Pour gérer le gain de focus différé, vous devez créer un OnAudioFocusChangeListener
avec une méthode de rappel onAudioFocusChange()
qui implémente le comportement souhaité et enregistre l'écouteur en appelant setOnAudioFocusChangeListener()
.
Priorité audio dans Android 7.1 et versions antérieures
Lorsque vous appelez requestAudioFocus()
, vous devez spécifier un indice de durée, qui peut être respecté par une autre application qui est actuellement active et en cours de lecture :
- Demandez une priorité audio permanente (
AUDIOFOCUS_GAIN
) lorsque vous prévoyez de lire de l'audio dans un avenir prévisible (par exemple, lorsque vous écoutez de la musique) et que vous vous attendez à ce que le détenteur précédent de la priorité audio arrête la lecture. - Demandez une priorité audio transitoire (
AUDIOFOCUS_GAIN_TRANSIENT
) lorsque vous prévoyez de lire de l'audio pendant une courte période uniquement et que vous vous attendez à ce que le détenteur précédent mette la lecture en pause. - Demandez une priorité temporaire avec ducking (
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
) pour indiquer que vous prévoyez de lire de l'audio pendant une courte période uniquement et que l'ancien propriétaire de la priorité peut continuer à lire si le volume de sa sortie audio est réduit. Les deux sorties audio sont mixées dans le flux audio. L'atténuation est particulièrement adaptée aux applications qui utilisent le flux audio de manière intermittente, par exemple pour les instructions de navigation vocales.
La méthode requestAudioFocus()
nécessite également un AudioManager.OnAudioFocusChangeListener
. Cet écouteur doit être créé dans l'activité ou le service qui possède votre session multimédia. Il implémente le rappel onAudioFocusChange()
que votre application reçoit lorsqu'une autre application acquiert ou abandonne le focus audio.
L'extrait de code suivant demande une mise au point audio permanente sur le flux STREAM_MUSIC
et enregistre un OnAudioFocusChangeListener
pour gérer les modifications ultérieures de la mise au point audio. (L'écouteur de modification est abordé dans Répondre à une modification de la priorité 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 }
Lorsque la lecture est terminée, appelez abandonAudioFocus()
.
Kotlin
audioManager.abandonAudioFocus(afChangeListener)
Java
// Abandon audio focus when playback complete audioManager.abandonAudioFocus(afChangeListener);
Cela indique au système que vous n'avez plus besoin de la mise au point et annule l'enregistrement de l'OnAudioFocusChangeListener
associé. Si vous avez demandé une mise au point transitoire, cela avertit une application qui a mis en pause ou baissé le volume qu'elle peut continuer à lire ou restaurer son volume.
Répondre à un changement de priorité audio
Lorsqu'une application acquiert la priorité audio, elle doit pouvoir la libérer lorsqu'une autre application la demande pour elle-même. Dans ce cas, votre application reçoit un appel à la méthode onAudioFocusChange()
dans le AudioFocusChangeListener
que vous avez spécifié lorsque l'application a appelé requestAudioFocus()
.
Le paramètre focusChange
transmis à onAudioFocusChange()
indique le type de modification en cours. Elle correspond à l'indication de durée utilisée par l'application qui acquiert le focus. Votre application doit répondre de manière appropriée.
- Perte de concentration temporaire
-
Si le changement de focus est transitoire (
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
ouAUDIOFOCUS_LOSS_TRANSIENT
), votre application doit baisser le volume (si vous ne vous fiez pas à la baisse automatique du volume) ou mettre la lecture en pause, mais conserver le même état.Lors d'une perte temporaire de la priorité audio, vous devez continuer à surveiller les changements de priorité audio et être prêt à reprendre la lecture normale lorsque vous récupérez la priorité. Lorsque l'application de blocage abandonne la mise au point, vous recevez un rappel (
AUDIOFOCUS_GAIN
). À ce stade, vous pouvez rétablir le volume à un niveau normal ou redémarrer la lecture. - Perte définitive de concentration
-
Si la perte de priorité audio est permanente (
AUDIOFOCUS_LOSS
), une autre application lit du contenu audio. Votre application doit suspendre la lecture immédiatement, car elle ne recevra jamais de rappelAUDIOFOCUS_GAIN
. Pour redémarrer la lecture, l'utilisateur doit effectuer une action explicite, comme appuyer sur le bouton de lecture dans une notification ou l'interface utilisateur de l'application.
L'extrait de code suivant montre comment implémenter OnAudioFocusChangeListener
et son rappel onAudioFocusChange()
. Notez l'utilisation d'un Handler
pour retarder le rappel d'arrêt en cas de perte permanente du focus 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 } } };
Le gestionnaire utilise un Runnable
qui se présente comme suit :
Kotlin
private var delayedStopRunnable = Runnable { mediaController.transportControls.stop() }
Java
private Runnable delayedStopRunnable = new Runnable() { @Override public void run() { getMediaController().getTransportControls().stop(); } };
Pour vous assurer que l'arrêt différé ne se déclenche pas si l'utilisateur redémarre la lecture, appelez mHandler.removeCallbacks(mDelayedStopRunnable)
en réponse à tout changement d'état. Par exemple, appelez removeCallbacks()
dans les méthodes onPlay()
, onSkipToNext()
, etc. de votre rappel. Vous devez également appeler cette méthode dans le rappel onDestroy()
de votre service lorsque vous nettoyez les ressources utilisées par votre service.