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() 回调中调用此方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/564395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

neo4j browser执行脚本后不提示用时_还不懂什么是分层自动化测试的,有赞的实践经历告诉你...

来源:https://testerhome.com/articles/19109# 背景先理一下自动化测试的概念,从广义上来说,一切通过工具(程序)的方式来代替或者辅助手工测试的行为都可以成为自动化。从狭义上来说,通过编写脚本的方式,模拟手工测试…

android+自定义alertdialog,安卓自定义AlertDialog

AlertDialog.png使用方法 example:DialogUtil.showAlertDialog(getActivity(), R.mipmap.restart, "退出提示", "你确定要退出吗?"),"确定", "取消", true, new DialogUtil.AlertDialogBtnClickListener() {Overridepublic…

eclipse跳转到指定行快捷键_用什么快捷键可以跳到下一个一样的?

Ctrl Shift O: 引入imports语句Ctrl Shift T: 打开Open Type查找类文件Ctrl Shift F4: 关闭打开的所有窗口Ctrl Shift F: 整形Ctrl Alt ↓(↑) : 向下(上)复制本行 (搞笑)Ctrl D : 删除本行Ctrl O: Open declarations F3 : Open DeclarationCtrl E : 打开编辑器(切…

android 跟随动画,Android实现View拖拽跟随手指移动效果

今天想实现这个功能,但是网上搜索代码,都是利用setPadding,setMargin 等方法去实现的,这在Android 4.0 以前是没问题的,但是,android 4.0 后系统已经提供了更简单的方法给我们用了,就是setTrans…

mysql datetime 后面带了很多0_面试官:MySQL 表设计要注意什么?

作者 孤独烟来自公众号:孤独烟引言大家应该知道烟哥最近要(tiao 咳咳咳),嗯,不可描述!随手讲其中一部分知识,都是一些烟哥自己平时工作的总结以及经验。大家看完,其实能避开很多坑。而且很多问题&#xff0…

poi 顺序解析word_JavaPOI解析word提取数据到excel

Java POI解析Word提取数据存储在Excel一、了解POIPOI以前有了解,这次需求是解析word读取其中标题,还有内容赛选获取自己想要的内容经过两天的学习,开始熟悉Java这么读取word和解析。本文中运用是读取整个页面模块的range,通过对ra…

android studio viewo,Android Studio 之 ViewModel

ViewModel 是 JetPack 类库中的一个功能,可以保存控件的状态 ,在整个Activity 生命周期中,状态不会失效如屏幕翻转时,状态可保留,不会失效!与 LiveData 配合使用!配合 Room 进行 Sqlite 操作数据…

git安装 perl ubuntu_ubuntu下安装git

最近在做自己的个人博客项目,部署在阿里云主机上,系统为ubuntu 16.04.4。项目开发在自己的Windows电脑上,每次项目进行改动后都需要手动上传文件到服务器上,感觉很是麻烦。所以准备在服务器上安装git并关联github账号,…

android 图片传递,如何使用包在Android活动之间传递图像(位图)?

按照EboMike的建议,我将位图保存在一个名为MyImage在我的应用程序的内部存储中,无法访问我的其他应用程序。这部分的代码如下:public String createImageFromBitmap(Bitmap bitmap) {String fileName "myImage";//no .png or .jpg…

php调用restful接口_分享一个PHP调用RestFul接口的函数

/*** [http 调用接口函数]* Date 2016-07-11* Author GeorgeHao* param string $url [接口地址]* param array $params [数组]* param string $method [GET\POST\DELETE\PUT]* param array $header [HTTP头信息]* param integer $timeout [超时时间]* return [type] [接口返回数…

graphpad如何换柱状图与折线图能否混合一起_excel柱状图加折线图组合怎么做,原来是这样的...

今天小编教大家如Excel柱状图加折线图组合怎么做。操作方法01以下图表格为例,我们就用这个年份、销量和增长率来做个柱状图与折线图的组合形式图表。首先,拖动鼠标,选中销量和增长率两项的所有数据。02选中数据后,点击上面菜单栏中…

ucache灾备云报价_UCACHE灾备云功能

(IDC彭帅)未来互联网、移动互联网、物联网、工业互联网行业将迎来迅猛发展,作为数据安全最后一道防线,灾备技术具有巨大的应用前景。当前,企业的每一个业务系统所关心的最主要问题就是业务如何连续运转的问题,这其中,既…

html下拉框选择后自动刷新,html select 下拉框刷新页面后保留上一次选择的值

常用场景组合条件查询点击查询/刷新页面,包括input输入框\复选框等在内的组件都可以通过前端设置value"{{id}}",后台发送的数据包括对应的字段,从而实现刷新后保留上一次的值,提高用户体验,但是发现select不…

极域电子书包课堂管理系统_朝阳群众说小康 | 从黑板课本到VR互动课堂、电子书包,朝阳的课堂如此有趣!...

教育变迁一支粉笔、一块黑板、一本教材曾经是教师上课沿袭了几十年的“三大法宝”随着时代发展当科技遇上了教育课堂上又会擦出什么样的火花呢?今天,小朝带你走进咱朝阳的校园一探究竟不一young的朝阳教育近日,教育部“基于教学改革、融合信息…

MySQL的优点

MySQL 使用的 SQL 语言是用于访问数据库的最常用的标准化语言。 由于 MySQL 数据库体积小、速度快、总体拥有成本低、开放源代码,其有着广泛的应用,一般中小型网站的开发都选择 MySQL 作为网站数据库。由于其社区版的性能卓越,因此搭配 PHP …

鸿蒙行车记录仪,百度导航新增行车记录仪功能 可消除碰瓷风险

年关将至,市区内各类大型商场、超市、菜市场等地人流密集,此类地点非常容易出现意外状况,市民在驾车出行时千万要提高注意力,警惕碰瓷者倒在你面前。如果事先装载行车记录仪,就能避免一桩“冤案”的发生。日前&#xf…

安装引导黑屏_给电脑安装系统老是装不上,重启就黑屏,原来是这项设置在作怪!...

很多人和我反映说:给电脑安装系统重启电脑后就黑屏无法正确解压系统,这个问题大家有没有遇到呢?遇到这个问题的人可能会认为自己电脑的硬盘坏了,明明已经把需要的Windows操作系统拷贝到硬盘已经成功了,重启电脑准备解压…

MySQL创建数据库

MySQL 中&#xff0c;我们可以使用 CREATE DATABASE 语句创建数据库&#xff0c;语法格式如下&#xff1a; CREATE DATABASE [IF NOT EXISTS] <数据库名> [[DEFAULT] CHARACTER SET <字符集名>] [[DEFAULT] COLLATE <校对规则名>];[ ]中的内容是可选的。语…

如果表不存在则创建_当创建一个文件的时候,操作系统发生了什么

操作文件是我们平时经常有的操作。但是我们可能并不是很了解他们原理&#xff0c;比如为什么删除一个很大的文件&#xff0c;会非常快&#xff1f;创建一个文件的时候&#xff0c;系统发生了什么&#xff1f;为什么删除的文件&#xff0c;还可以恢复&#xff1f;知其然知其所以…

两个html页面之间通讯,面试官:前端跨页面通信,你知道哪些方法?

引言在浏览器中&#xff0c;我们可以同时打开多个Tab页&#xff0c;每个Tab页可以粗略理解为一个“独立”的运行环境&#xff0c;即使是全局对象也不会在多个Tab间共享。然而有些时候&#xff0c;我们希望能在这些“独立”的Tab页面之间同步页面的数据、信息或状态。正如下面这…