两个或两个以上的 Android 应用可同时向同一输出流播放音频。系统会将所有音频流混合在一起。虽然这是一项出色的技术,但却会给用户带来很大的困扰。为了避免所有音乐应用同时播放,Android 引入了“音频焦点”的概念。

一次只能有一个应用获得音频焦点。

当您的应用需要输出音频时,它需要请求获得音频焦点,获得焦点后,就可以播放声音了。不过,在您获得音频焦点后,您可能无法将其一直持有到播放完成。其他应用可以请求焦点,从而占有您持有的音频焦点。如果发生这种情况,您的应用应暂停播放或降低音量,以便于用户听到新的音频源。

音频焦点采用合作模式。我们建议应用遵守音频焦点准则,但系统不会强制执行这些准则。如果应用想要在失去音频焦点后继续大声播放,系统无法阻止它。这是一种不好的体验,用户很可能会卸载具有这种不良行为的应用。

行为恰当的音频应用应根据以下一般准则来管理音频焦点:

在即将开始播放之前调用 requestAudioFocus(),并验证调用是否返回 onPlay() 回调中调用 requestAudioFocus()。

在其他应用获得音频焦点时,停止或暂停播放,或降低音量。

播放停止后,放弃音频焦点。

运行的 Android 版本不同,音频焦点的处理方式也会不同:

对于以 Android 5.0(API 级别 21)及更高版本为目标平台的应用,音频应用应使用

面向 Android 8.0(API 级别 26)或更高版本的应用应使用 AudioFocusRequest 包含有关应用的音频上下文和功能的信息。系统使用这些信息来自动管理音频焦点的得到和失去。

Android 8.0 及更高版本中的音频焦点

从 Android 8.0(API 级别 26)开始,当您调用 AudioFocusRequest 参数。要释放音频焦点,请调用 AudioFocusRequest 作为参数。在请求和放弃焦点时,应使用相同的 AudioFocusRequest 实例。

要创建

FocusGain 字段为必需字段;所有其他字段均为可选字段。

方法备注

每个请求中都必须包含此字段。此字段的值与 Android 8.0 之前的 requestAudioFocus() 调用中所使用的 durationHint 值相同:AUDIOFOCUS_GAIN、AUDIOFOCUS_GAIN_TRANSIENT、AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 或 AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE。

首先使用

如果未指定,则 AudioAttributes 默认为 AudioAttributes.USAGE_MEDIA。

当其他应用使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 请求焦点时,持有焦点的应用通常不会收到 自行降低音量。如果您需要暂停播放而不是降低音量,请调用 setWillPauseWhenDucked(true),然后创建并设置 OnAudioFocusChangeListener,具体如自动降低音量中所述。

当焦点被其他应用锁定时,对音频焦点的请求可能会失败。此方法可实现延迟获取焦点,即在焦点可用时异步获取焦点。

请注意,要使“延迟获取焦点”起作用,您还必须在音频请求中指定

只有当您在请求中还指定了 willPauseWhenDucked(true) 或 setAcceptsDelayedFocusGain(true) 时,才需要 OnAudioFocusChangeListener。

有两个方法可以设置监听器:一个带处理程序参数,一个不带。处理程序是运行监听器的线程。如果您未指定处理程序,则会使用与主

以下示例展示了如何使用 AudioFocusRequest.Builder 构建 AudioFocusRequest 来请求和放弃音频焦点:

Kotlin

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()

}

mediaPlayer = MediaPlayer()

val focusLock = Any()

var playbackDelayed = false

var playbackNowAuthorized = false

// ...

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

}

}

// ...

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) {

resumeOnFocusGain = true

playbackDelayed = false

}

pausePlayback()

}

AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {

// ... pausing or ducking depends on your app

}

}

}Java

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();

mediaPlayer = new MediaPlayer();

final Object focusLock = new Object();

boolean playbackDelayed = false;

boolean playbackNowAuthorized = false;

// ...

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;

}

}

// ...

@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) {

resumeOnFocusGain = true;

playbackDelayed = false;

}

pausePlayback();

break;

case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:

// ... pausing or ducking depends on your app

break;

}

}

}

自动降低音量

在 Android 8.0(API 级别 26)中,当其他应用使用 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 请求焦点时,系统可以在不调用应用的 onAudioFocusChange() 回调的情况下降低和恢复音量。

虽然自动降低音量的行为对于音乐和视频播放应用来说是可接受的,但在播放语音内容时(例如在听书应用中)就没什么用处了。在这种情况下,应用应该暂停播放。

如果您希望应用在被要求降低音量时暂停播放,请创建包含 onAudioFocusChange() 回调方法的 OnAudioFocusChangeListener,该回调方法可以实现所需的暂停/恢复行为。

调用

延迟获取焦点

在有些情况下,系统不能批准对音频焦点的请求,因为焦点被其他应用“锁定”了,例如在通话过程中。在这种情况下,requestAudioFocus() 会返回 AUDIOFOCUS_REQUEST_FAILED。在这种情况下,您的应用将不会播放音频,因为它未获得焦点。

方法 AUDIOFOCUS_REQUEST_DELAYED。当锁定音频焦点的情况不再存在时(例如当通话结束时),系统会批准待处理的焦点请求,并调用 onAudioFocusChange() 来通知您的应用。

为了处理“延迟获取焦点”,您必须创建包含 onAudioFocusChange() 回调方法的 OnAudioFocusChangeListener,该回调方法会通过调用

Android 8.0 之前的音频焦点

当您调用

如果您计划在可预见的将来播放音频(例如在播放音乐时),并且希望前一个持有音频焦点的应用停止播放,则应该请求永久性的音频焦点 (AUDIOFOCUS_GAIN)。

如果您只希望在短时间内播放音频,并且希望前一个持有音频焦点的应用暂停播放,则应该请求暂时性的焦点 (AUDIOFOCUS_GAIN_TRANSIENT)。

请求附带“降低音量”(AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) 的暂时性焦点,表示您只希望在短时间内播放音频,并允许前一个持有焦点的应用在降低其音频输出的情况下继续播放。这两个音频输出会混合到音频流中。降低音量特别适合于间歇性使用音频流的应用,例如有声的行车路线。

requestAudioFocus() 方法同样需要 onAudioFocusChange() 回调,您的应用会在其他应用获取或放弃音频焦点时收到该回调。

以下代码段会请求对 STREAM_MUSIC 流的永久性音频焦点,并注册 OnAudioFocusChangeListener 来处理音频焦点的后续更改。(有关更改监听器的说明,请参阅响应音频焦点更改。)

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

}

Kotlin

audioManager.abandonAudioFocus(afChangeListener)Java

// Abandon audio focus when playback complete

audioManager.abandonAudioFocus(afChangeListener);

这会通知系统您不再需要焦点,并注销关联的 OnAudioFocusChangeListener。如果您请求的是暂时性焦点,则会通知已暂停或降低音量的应用它可以继续播放或恢复其音量。

响应音频焦点更改

当应用获得音频焦点后,它必须能够在其他应用为自己请求音频焦点时释放该焦点。出现这种情况时,您的应用会收到对 AudioFocusChangeListener 中的 requestAudioFocus() 时指定的。

传递给 onAudioFocusChange() 的 focusChange 参数表示所发生的更改类型。它对应于获取焦点的应用所使用的持续时间提示。您的应用应该做出适当的响应。

暂时性失去焦点

如果焦点更改是暂时性的(AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 或 AUDIOFOCUS_LOSS_TRANSIENT),您的应用应该降低音量(如果您不依赖于自动降低音量)或暂停播放,否则保持相同的状态。

在暂时性失去音频焦点时,您应该继续监控音频焦点的变化,并准备好在重新获得焦点后恢复正常播放。当抢占焦点的应用放弃焦点时,您会收到一个回调 (AUDIOFOCUS_GAIN)。此时,您可以将音量恢复到正常水平或重新开始播放。永久性失去焦点

如果是永久性失去音频焦点 (AUDIOFOCUS_LOSS),则其他应用会播放音频。您的应用应立即暂停播放,因为它不会收到 AUDIOFOCUS_GAIN 回调。要重新开始播放,用户必须执行明确的操作,例如在通知或应用界面中按播放传输控件。

以下代码段展示了如何实现 OnAudioFocusChangeListener 及其 onAudioFocusChange() 回调。请注意这里使用 Handler 延迟了对永久性失去音频焦点的停止回调。

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

}

}

};

处理程序使用如下所示的 Runnable:

Kotlin

private var delayedStopRunnable = Runnable {

mediaController.transportControls.stop()

}Java

private Runnable delayedStopRunnable = new Runnable() {

@Override

public void run() {

getMediaController().getTransportControls().stop();

}

};

为了确保在用户重新开始播放时不会触发延迟停止,请调用 mHandler.removeCallbacks(mDelayedStopRunnable) 来响应任何状态变化。例如,在回调的 onPlay()、onSkipToNext() 等中调用 removeCallbacks()。此外,在清理服务使用的资源时,您也应该在服务的 onDestroy() 回调中调用此方法。

android汽车音频焦点方案,管理音频焦点  |  Android 开发者  |  Android Developers相关推荐

  1. Android官方开发文档Training系列课程中文版:管理音频播放之管理音频焦点

    原文地址:http://android.xsoftlab.net/training/managing-audio/audio-focus.html 因为可能会存在多个APP播放音频,所以考虑它们之间的 ...

  2. 创建多媒体APP 之 音频播放:管理音频焦点

    因为很多APP都会潜在需要去播放音频文件,所有要考虑多个APP在播放音频文件的时候会产生的影响,这个非常重要.为了防止同时有多个APP播放音频,android采用了音频焦点这个概念来节制APP对音频的 ...

  3. 好声方可入人心,数美科技智能音频审核方案为音频内容消费保驾护航

    语音,是人天然的沟通形式.相比起文字,语音所包含的顿挫.情感.语气.音量能够传递出更加直接和真切的信息.在历史中,人对传输语音也充满了渴望,从书信到电话.电台.唱片.磁带.光盘,人总是在用更前沿的技术 ...

  4. android防拆机方案,拆机狂人必备 iFixit Android客户端试用

    作者:Aimo 相信不少IT产品爱好者都知道iFixit,即使不知道的人在不经意见也会看到过iFixit的作品--iFixit是一家著名的IT产品维修网 站,新IT产品流出之时的第一手拆机资料往往都来 ...

  5. android触屏压力方案,触摸屏实现原理与在android上实现.doc

    PAGE 6 武汉工程大学邮电与信息工程学院毕业设计(论文) 武汉工程大学邮电与信息工程学院 毕业设计(论文) 触摸屏的实现原理及在android上的实现 The Principle of the T ...

  6. Unity中的音频资料总结-基本使用+音频可视化+音效管理

    入门 基础用法一 基础用法二 进阶 音频可视化 看着一个基本就够了 这个是拆解 简单使用 具体使用 好看的 也是对第一个视频的拆解分析 核心api就一句 GetOutputData 各种炫酷的效果 # ...

  7. Android系统中的进程管理:内存的回收

    本文是Android系统进程管理的第三篇文章.进程管理的前面两篇文章,请参见这里: Android系统中的进程管理:进程的创建 Android系统中的进程管理:进程的优先级 本文适合Android平台 ...

  8. Android音乐编程:管理音频焦点

    转自:http://mobile.51cto.com/android-309321.htm 因为系统中可能会有多个应用程序会播放音频,所以需要考虑他们之间该如何交互,为了避免多个应用程序同时播放音乐, ...

  9. Android官方开发文档Training系列课程中文版:管理音频播放之控制APP的音量与播放

    原文地址:http://android.xsoftlab.net/training/managing-audio/index.html 引言 如果APP需要播放音频,允许用户可以控制音频的播放状态是很 ...

最新文章

  1. gbdt xgboost 贼难理解!
  2. Angular页面调试一个有用的小技巧 - normalizeDebugBindingName和normalizeDebugBindingValue - [object Object]
  3. 程序员幽默:当代程序员的主要矛盾是什么?
  4. 樊昌信版通信原理期末复习第一章绪论
  5. 【Flink】Flink 1.12.2 TaskSlotTable
  6. 大数据之-Hadoop3.x_MapReduce_ETL数据清洗案例---大数据之hadoop3.x工作笔记0136
  7. ProcessStartInfo 例子
  8. 海军装备、舰船电子设备|环境可靠性试验|GJB150A\GJB4
  9. 登录oneNote失败解决
  10. windows server 2012 更改网络位置
  11. 3.5 计算机网络之介质访问控制(静态划分信道、FDM、TDM、STDM、WDM、CDM)、(动态划分信道、ALOHA、CSMA、CSMA/CD、CSMA/CA)、令牌传递协议
  12. windows系统使用cmd命令打开谷歌浏览器并设置用户资料方法步骤
  13. 利用摄像头实现人员活动检测(python+openCV)
  14. 贝塞尔曲线是什么?如何用 Canvas 绘制三阶贝塞尔曲线?
  15. JLink的RTT使用
  16. 网页加载慢,Initial connection请求偶尔过长
  17. oracle 6i字体变大,将iTunes 12字体大小更改为更大或更小 | MOS86
  18. 视频的码率、帧率、分辨率之间的区别
  19. The fifth day
  20. 延时加载全局的js和css

热门文章

  1. Win11任务栏大小怎么更改
  2. 修改TOMCAT的JVM虚拟机内存大小几种方式
  3. 中科院C语言应聘机试编程题6,中科院计算所保研笔试+机试+面试经验分享
  4. matlab人民币识别,MATLAB-OCR 用MATLAB实现人民币纸币金额的识别与统计 - 下载 - 搜珍网...
  5. angularjs input标签用一个日期插件后数据不能双向绑定了_微信如何定时发朋友圈?(最方便最好用的办法!)...
  6. Java hdfs连接池_Java使用连接池管理Hdfs连接
  7. normalize函数_Pandas 数据处理(一) —— 几个简单函数掌握!
  8. 如何将一个向量投影到一个平面上_CameraLidar投影:2D3D导航
  9. c primer英文版第5版_2019足坛10大巨星:梅西第2范戴克第3,C罗第5
  10. 卡尔曼_卡尔曼滤波最完整公式推导