android音视频指南-管理音频焦点

栏目: Android · 发布时间: 6年前

内容简介:翻译自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()回调中调用此方法。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming PHP

Programming PHP

Rasmus Lerdorf、Kevin Tatroe、Peter MacIntyre / O'Reilly Media / 2006-5-5 / USD 39.99

Programming PHP, 2nd Edition, is the authoritative guide to PHP 5 and is filled with the unique knowledge of the creator of PHP (Rasmus Lerdorf) and other PHP experts. When it comes to creating websit......一起来看看 《Programming PHP》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试