内容简介:翻译自Managing audio focus两个或更多的Android应用程序可以同时播放音频到相同的输出流。系统把所有东西混合在一起。虽然这在技术上是令人印象深刻的,但对用户来说却是非常令人恼火的。为了避免所有音乐应用同时播放,Android引入了音频聚焦的概念。只有一个应用程序可以一次聚焦音频。当您的应用程序需要输出音频时,它应该请求音频焦点。当它有焦点时,它可以播放声音。然而,在你获得音频焦点后,你可能无法持有它直到你播放完。另一个应用程序可以请求焦点,它会抢占你的音频焦点。如果出现这种情况,你的
翻译自Managing audio focus
两个或更多的Android应用程序可以同时播放音频到相同的输出流。系统把所有东西混合在一起。虽然这在技术上是令人印象深刻的,但对用户来说却是非常令人恼火的。为了避免所有音乐应用同时播放,Android引入了音频聚焦的概念。只有一个应用程序可以一次聚焦音频。
当您的应用程序需要输出音频时,它应该请求音频焦点。当它有焦点时,它可以播放声音。然而,在你获得音频焦点后,你可能无法持有它直到你播放完。另一个应用程序可以请求焦点,它会抢占你的音频焦点。如果出现这种情况,你的应用程序应该暂停播放或降低音量,让用户更容易听到新的音频源。
音频聚焦是合作的。鼓励应用程序遵守音频聚焦指南,但该系统不执行这些规则。如果一个应用程序想在失去声音焦点后继续大声播放,没有什么能阻止它。这是一种糟糕的体验,用户很有可能会卸载出现这种糟糕体验的应用程序。
一个表现良好的音频应用程序应该管理音频焦点根据这些一般准则:
-
在开始播放之前立即调用
requestAudioFocus()
,并验证调用是否返回 AUDIOFOCUS_REQUEST_GRANTED 。如果您按照我们在本指南中描述的那样设计应用程序,那么应该在媒体会话的onPlay()
回调中调用requestAudioFocus()
。 - 当另一个应用程序获得音频焦点时,停止或暂停播放,或降低音量。
- 当播放停止,放弃音频焦点。
音频焦点的处理方式因Android版本不同而异:
- 从Android 2.2 (API level 8)开始,应用程序通过调用requestAudioFocus()和abandonAudioFocus()来管理音频焦点。应用程序还必须注册一个 AudioManager.OnAudioFocusChangeListener ,以接收回调和管理自己的音频级别。
- 对于Android 5.0 (API level 21)或更高版本的应用程序,音频应用程序应该使用AudioAttributes来描述你的应用程序正在播放的音频类型。例如,播放语音的应用程序应该指定CONTENT_TYPE_SPEECH。
-
运行Android 8.0 (API级别26)或更高版本的应用程序应该使用
requestAudioFocus()
方法,它接受AudioFocusRequest参数。AudioFocusRequest包含关于音频上下文和应用程序功能的信息。系统使用这些信息自动管理音频焦点的获取和丢失。
在Android 8.0和更高版本上的音频聚焦
从Android 8.0 (API级别26)开始,当您调用requestAudioFocus()时,必须提供AudioFocusRequest参数。若要释放音频焦点,请调用abandonAudioFocusRequest()方法,该方法还将AudioFocusRequest作为其参数。当请求和放弃焦点时,应该使用相同的AudioFocusRequest实例。
要创建AudioFocusRequest,请使用AudioFocusRequest. builder。由于焦点请求必须始终指定请求的类型,因此该类型包含在构造器的构造函数中。使用构建器的方法设置请求的其他字段。
需要 FocusGain
字段;所有其他字段都是可选的。
解释一下主要方法:
每个请求都需要这个字段。它的值与android8.0之前对requestaudiofococus()的调用中使用的durationHint相同: AUDIOFOCUS_GAIN
, AUDIOFOCUS_GAIN_TRANSIENT
, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
,或 AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
。
audioattributes描述了系统应用程序的用例。当一个应用程序获得和失去了音频的焦点时系统会查看他们。属性取代了流类型的概念。在Android 8.0 (API级别26)及更高版本中,除了音量控制之外的任何操作都不支持流类型。在focus请求中使用与音频播放器中相同的属性(如下表所示)。
使用一个 AudioAttributes.Builder 首先指定属性,然后使用此方法将属性分配给请求。
如果没有指定,AudioAttributes默认为 AudioAttributes.USAGE_MEDIA
。
当另一个应用程序请求使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
进行焦点处理时,具有焦点的应用程序通常不会收到onAudioFocusChange()回调,因为系统可以自己执行这个操作。当你需要暂停播放而不是降低音量时,调用setWillPauseWhenDucked(true),创建并设置一个OnAudioFocusChangeListener,如下面的自动ducking所述。
当焦点被另一个应用锁定时,对音频焦点的请求可能会失败。这种方法允许延迟焦点增益:当焦点可用时,异步获取焦点的能力。
注意,只有当你指定了一个AudioManager.OnAudioFocusChangeListener时,延迟的焦点增益(focus gain)才有效。因为您的应用程序需要接收回调以便知道焦点已被允许获取。
只有在请求中指定willPauseWhenDucked(true)或setAcceptsDelayedFocusGain(true)时,才需要OnAudioFocusChangeListener。
设置侦听器有两种方法:一种有处理程序参数,另一种没有处理程序参数。处理程序是侦听器运行的线程。如果不指定处理程序,则使用与主Looper关联的处理程序(handler)。
下面的例子展示了如何使用AudioFocusRequest.Bulder构建一个AudioFocusRequest还有请求和放弃音频焦点:
mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE); mPlaybackAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_GAME) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(mPlaybackAttributes) .setAcceptsDelayedFocusGain(true) .setOnAudioFocusChangeListener(afChangeListener, mHandler) .build(); mMediaPlayer = new MediaPlayer(); final Object mFocusLock = new Object(); boolean mPlaybackDelayed = false; boolean mPlaybackNowAuthorized = false; // ... int res = mAudioManager.requestAudioFocus(mFocusRequest); synchronized(mFocusLock) { if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { mPlaybackNowAuthorized = false; } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { mPlaybackNowAuthorized = true; playbackNow(); } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { mPlaybackDelayed = true; mPlaybackNowAuthorized = false; } } // ... @Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: if (mPlaybackDelayed || mResumeOnFocusGain) { synchronized(mFocusLock) { mPlaybackDelayed = false; mResumeOnFocusGain = false; } playbackNow(); } break; case AudioManager.AUDIOFOCUS_LOSS: synchronized(mFocusLock) { mResumeOnFocusGain = false; mPlaybackDelayed = false; } pausePlayback(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: synchronized(mFocusLock) { mResumeOnFocusGain = true; mPlaybackDelayed = false; } pausePlayback(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // ... pausing or ducking depends on your app break; } } } 复制代码
自动ducking
在Android 8.0 (API级别26)中,当另一个应用程序使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
请求焦点时,系统可以降低音量并恢复音频播放,而无需调用应用程序的 onAudioFocusChange()
回调。
在音乐和视频播放应用程序中,自动降低音量是可以接受的行为,但在播放语音内容(比如在有声书应用程序中)时,它就没用了。在这种情况下,应用程序应该暂停。
如果你想让你的应用程序在被要求ducking时暂停而不是减少它的音量,创建一个 OnAudioFocusChangeListener
,使用一个 onAudioFocusChange()
回调方法来实现所需的暂停/恢复行为。调用setOnAudioFocusChangeListener()来注册侦听器,并调用setWillPauseWhenDucked(true)来告诉系统使用您的回调,而不是执行自动ducking操作。
延迟获得焦点
有时系统不能批准音频焦点请求,因为焦点被另一个应用程序“锁定”,比如在打电话时。在本例中, requestAudioFocus()
返回 AUDIOFOCUS_REQUEST_FAILED
。当这种情况发生时,您的应用程序不应该继续进行音频播放,因为它没有获得焦点。
该方法名为setAcceptsDelayedFocusGain(true),它允许应用程序异步处理焦点请求。设置此标志后,锁定焦点时发出的请求将返回 AUDIOFOCUS_REQUEST_DELAYED
。当锁定音频焦点的条件不再存在时,例如当电话结束时,系统会授予挂起的焦点请求,并调用onAudioFocusChange()来通知应用程序。
为了处理延迟的焦点增益,您必须创建一个OnAudioFocusChangeListener,它使用一个onAudioFocusChange()回调方法来实现所需的行为,并通过调用setOnAudioFocusChangeListener()来注册侦听器。
android 8.0之前版本 音频聚焦
当你调用requestAudioFocus()时,你必须指定一个duration hint,这个duration hint可能会被另一个当前保持焦点并播放的应用程序使用:
-
当你计划在可预见的将来播放音频时(例如,播放音乐时)请求永久的音频焦点(AUDIOFOCUS_GAIN),并且您希望先前的音频焦点持有者停止播放。
-
请求瞬态焦点(AUDIOFOCUS_GAIN_TRANSIENT),当您希望只在短时间内播放音频,而您希望之前的持有者暂停播放。
-
请求ducking瞬态焦点 (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK),以指示您希望只在短时间内播放音频,并且如果先前的焦点所有者“duck”(降低)其音频输出,仍可以继续播放着。两个音频输出都混合到音频流中。Ducking特别适用于间歇性使用音频流的应用程序,比如声音驱动方向。
requestAudioFocus()方法也需要一个AudioManager.OnAudioFocusChangeListener。这个侦听器应该创建在你的媒体会话所处的相同活动或服务中。它实现了回调onAudioFocusChange(),当其他应用程序获得或放弃音频焦点时,您的应用程序会接收到这个回调。
以下代码片段请求流STREAM_MUSIC上的永久音频焦点,并注册一个OnAudioFocusChangeListener来处理音频焦点中的后续更改。(在下面的 对音频焦点变化的响应 部分讨论了更改侦听器。)
AudioManager mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); AudioManager.OnAudioFocusChangeListener afChangeListener; ... // Request audio focus for playback int result = am.requestAudioFocus(afChangeListener, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // Start playback } 复制代码
当你完成播放时,调用abandonAudioFocus().
// Abandon audio focus when playback complete mAudioManager.abandonAudioFocus(afChangeListener); 复制代码
这将通知系统您不再需要焦点,并注销相关的OnAudioFocusChangeListener。如果您请求瞬态焦点,这将通知暂停或duck的应用程序可能继续播放或恢复音量。
对音频焦点变化的响应
当一个应用程序获得音频焦点时,它必须能够在另一个应用程序请求自己的音频焦点时释放它。当发生这种情况时,您的应用程序在AudioFocusChangeListener中接收到对onAudioFocusChange()方法的调用,该调用是在调用requestAudioFocus()时指定的。
传递给onAudioFocusChange()的focusChange参数指示正在发生的变化。它对应于应用程序获取焦点所使用的持续时间提示。你的应用程序应该做出适当的响应。
瞬态失焦
如果焦点的变化是瞬态的(AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK或AUDIOFOCUS_LOSS_TRANSIENT),应用程序应该ducking降低音量(如果你不依赖自动ducking)或暂停播放,但以其他方式保持相同的状态。
在音频焦点暂时消失期间,您应该继续监视音频焦点的变化,并准备在恢复焦点后恢复正常回放。当阻塞应用程序放弃焦点时,您将收到一个回调( AUDIOFOCUS_GAIN
)。此时,您可以将音量恢复到正常水平或重新开始播放。
永久失去焦点
如果音频焦点丢失是永久性的( AUDIOFOCUS_LOSS
),另一个应用程序正在播放音频。你的应用应该立即暂停播放,因为它永远不会收到 AUDIOFOCUS_GAIN
回调。要重新启动回放,用户必须采取显式操作,比如在通知或应用程序UI中按下play传输控件。
下面的代码片段演示了如何实现OnAudioFocusChangeListener及其onAudioFocusChange()回调。请注意,使用一个Handler在永久失去音频焦点时以延迟执行停止操作的回调。
private Handler mHandler = 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 mHandler.postDelayed(mDelayedStopRunnable, 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 } } }; 复制代码
handler使用一个如下所示的Runnable
private Runnable mDelayedStopRunnable = new Runnable() { @Override public void run() { getMediaController().getTransportControls().stop(); } }; 复制代码
如果用户重新启动播放,为了确保延迟停止不会起作用,可以调用 mHandler.removeCallbacks(mDelayedStopRunnable)
来响应任何状态更改。例如,在回调的onPlay()、onSkipToNext()中调用removeCallbacks()。在清理服务使用的资源时,还应该在服务的onDestroy()回调中调用此方法。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 声网 Agora 音频互动 MoS 分方法:为音频互动体验进行实时打分
- 用音频引导玩家 详解VR空间音频的重要性及使用方法
- 用音频引导玩家 详解VR空间音频的重要性及使用方法
- VLOOK V9.19:为 Markdown 带来了音频、视频的支持,还有 mini 模式音频播放
- Python爬虫音频数据
- iOS- 音频进度条
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
迎接互联网的明天
邹静 / 电子工业 / 2011-6 / 55.00元
《迎接互联网的明天-玩转3D Web(附盘)》,全书共5章,第1章主要阐述了国内外空前繁荣的3D互联网技术领域,以及这些领域透射出来的潜在商机;第2章主要用当下比较流行的Flash编程语言ActionScript 3,来向大家介绍面向对象编程语言的思想概念,以及一些3D渲染技术的入门知识;第3章注重建模知识的运用,主要运用WireFusion和3ds Max来制作3D网页;第4章主要介绍3D游戏编......一起来看看 《迎接互联网的明天》 这本书的介绍吧!