android汽车音频焦点方案,管理音频焦点 | Android 开发者 | Android Developers
两个或两个以上的 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相关推荐
- Android官方开发文档Training系列课程中文版:管理音频播放之管理音频焦点
原文地址:http://android.xsoftlab.net/training/managing-audio/audio-focus.html 因为可能会存在多个APP播放音频,所以考虑它们之间的 ...
- 创建多媒体APP 之 音频播放:管理音频焦点
因为很多APP都会潜在需要去播放音频文件,所有要考虑多个APP在播放音频文件的时候会产生的影响,这个非常重要.为了防止同时有多个APP播放音频,android采用了音频焦点这个概念来节制APP对音频的 ...
- 好声方可入人心,数美科技智能音频审核方案为音频内容消费保驾护航
语音,是人天然的沟通形式.相比起文字,语音所包含的顿挫.情感.语气.音量能够传递出更加直接和真切的信息.在历史中,人对传输语音也充满了渴望,从书信到电话.电台.唱片.磁带.光盘,人总是在用更前沿的技术 ...
- android防拆机方案,拆机狂人必备 iFixit Android客户端试用
作者:Aimo 相信不少IT产品爱好者都知道iFixit,即使不知道的人在不经意见也会看到过iFixit的作品--iFixit是一家著名的IT产品维修网 站,新IT产品流出之时的第一手拆机资料往往都来 ...
- android触屏压力方案,触摸屏实现原理与在android上实现.doc
PAGE 6 武汉工程大学邮电与信息工程学院毕业设计(论文) 武汉工程大学邮电与信息工程学院 毕业设计(论文) 触摸屏的实现原理及在android上的实现 The Principle of the T ...
- Unity中的音频资料总结-基本使用+音频可视化+音效管理
入门 基础用法一 基础用法二 进阶 音频可视化 看着一个基本就够了 这个是拆解 简单使用 具体使用 好看的 也是对第一个视频的拆解分析 核心api就一句 GetOutputData 各种炫酷的效果 # ...
- Android系统中的进程管理:内存的回收
本文是Android系统进程管理的第三篇文章.进程管理的前面两篇文章,请参见这里: Android系统中的进程管理:进程的创建 Android系统中的进程管理:进程的优先级 本文适合Android平台 ...
- Android音乐编程:管理音频焦点
转自:http://mobile.51cto.com/android-309321.htm 因为系统中可能会有多个应用程序会播放音频,所以需要考虑他们之间该如何交互,为了避免多个应用程序同时播放音乐, ...
- Android官方开发文档Training系列课程中文版:管理音频播放之控制APP的音量与播放
原文地址:http://android.xsoftlab.net/training/managing-audio/index.html 引言 如果APP需要播放音频,允许用户可以控制音频的播放状态是很 ...
最新文章
- gbdt xgboost 贼难理解!
- Angular页面调试一个有用的小技巧 - normalizeDebugBindingName和normalizeDebugBindingValue - [object Object]
- 程序员幽默:当代程序员的主要矛盾是什么?
- 樊昌信版通信原理期末复习第一章绪论
- 【Flink】Flink 1.12.2 TaskSlotTable
- 大数据之-Hadoop3.x_MapReduce_ETL数据清洗案例---大数据之hadoop3.x工作笔记0136
- ProcessStartInfo 例子
- 海军装备、舰船电子设备|环境可靠性试验|GJB150A\GJB4
- 登录oneNote失败解决
- windows server 2012 更改网络位置
- 3.5 计算机网络之介质访问控制(静态划分信道、FDM、TDM、STDM、WDM、CDM)、(动态划分信道、ALOHA、CSMA、CSMA/CD、CSMA/CA)、令牌传递协议
- windows系统使用cmd命令打开谷歌浏览器并设置用户资料方法步骤
- 利用摄像头实现人员活动检测(python+openCV)
- 贝塞尔曲线是什么?如何用 Canvas 绘制三阶贝塞尔曲线?
- JLink的RTT使用
- 网页加载慢,Initial connection请求偶尔过长
- oracle 6i字体变大,将iTunes 12字体大小更改为更大或更小 | MOS86
- 视频的码率、帧率、分辨率之间的区别
- The fifth day
- 延时加载全局的js和css
热门文章
- Win11任务栏大小怎么更改
- 修改TOMCAT的JVM虚拟机内存大小几种方式
- 中科院C语言应聘机试编程题6,中科院计算所保研笔试+机试+面试经验分享
- matlab人民币识别,MATLAB-OCR 用MATLAB实现人民币纸币金额的识别与统计 - 下载 - 搜珍网...
- angularjs input标签用一个日期插件后数据不能双向绑定了_微信如何定时发朋友圈?(最方便最好用的办法!)...
- Java hdfs连接池_Java使用连接池管理Hdfs连接
- normalize函数_Pandas 数据处理(一) —— 几个简单函数掌握!
- 如何将一个向量投影到一个平面上_CameraLidar投影:2D3D导航
- c primer英文版第5版_2019足坛10大巨星:梅西第2范戴克第3,C罗第5
- 卡尔曼_卡尔曼滤波最完整公式推导