Android 音频焦点管理
前言
前面写过一篇类似的文章,没写完,今天再详细描述一下。
Android音频焦点申请处理
音频焦点管理的意义
两个或两个以上的 Android 应用可同时向同一输出流播放音频。系统会将所有音频流混合在一起。虽然这是一项出色的技术,但却会给用户带来很大的困扰。为了避免所有音乐应用同时播放,Android 引入了“音频焦点”的概念。 一次只能有一个应用获得音频焦点。
当您的应用需要输出音频时,它需要请求获得音频焦点,获得焦点后,就可以播放声音了。不过,在您获得音频焦点后,您可能无法将其一直持有到播放完成。其他应用可以请求焦点,从而占有您持有的音频焦点。如果发生这种情况,您的应用应暂停播放或降低音量,以便于用户听到新的音频源。
音频焦点管理的行为准则
- 在即将开始播放之前调用 requestAudioFocus(),并验证调用是否返回 AUDIOFOCUS_REQUEST_GRANTED。
- 在其他应用获得音频焦点时,应该停止或者暂停播放,或者降低音量。
- 播放停止后应该放弃音频焦点
版本兼容
从Android 8.0(O版本,API 26)开始,音频焦点的请求方式以及系统管理有了细微的变化,下面分两部分来说明。
在Android 8.0(API 26) 之前对音频焦点具体处理实现
当想录音或者播放歌曲的时候,最好(非必须)先请求音频焦点,这个时候需要调用AudioManager.requestAudioFocus()方法,函数原型如下
AudioManager.requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)
第一个参数用于监听焦点变化;
第二个参数表明请求的音频焦点影响的是那种类型流;例如,如果我们录音,我们肯定是要影响Music这一类型的音频流,因此可以选择AudioManager.STREAM_MUSIC。当然还有许多类型:
/** 通话相关 */
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
/** 系统声音 */
public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
/** 铃声 */
public static final int STREAM_RING = AudioSystem.STREAM_RING;
/** 音乐相关 */
public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
/** 闹钟相关 */
public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
第三个参数用于表明音频焦点的持续时间,这个很关键,它也有许多种类型,下面一一列出。
- AudioManager.AUDIOFOCUS_GAIN: API文档说请求的这类音频焦点持续时间是未知的,通常用来表示长时间获取焦点,可以用来播放音乐,录音等等。
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 表明请求的音频焦点持续时间比较短,通常用来播放导航路线的声音,或者播放通知声音。
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 这个也是表明请求的音频焦点持续时间比较短,但是在这段时间内,它希望其他应用以较低音量继续播放。例如,我们在使用导航的时候可以听音乐,当出现导航语音的时候,音乐音量会降低以便我们能听清楚导航的语音,当导航语音播放完毕后,音乐恢复音量,继续播放。
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 这个也是表明音请求的音频焦点持续时间比较短,但是在这段时间内,不希望任何应用(包括系统应用)来做任何与音频相关的事情,就算是降低音量播放音乐也是不被希望的。例如当我们进行录音或者语音识别的时候,我们不希望其他的声音出现干扰。
AudioManager.requestAudioFocus()的返回值表明请求的结果AudioManager.AUDIOFOCUS_REQUEST_FAILED表明请求焦点失败;AudioManager.AUDIOFOCUS_REQUEST_GRANTED表明请求焦点成功。
当我们成功请求焦点后,就可以做一些与音频有关的事情,例如播放音乐,录音,或者语音识别。完成这些工作后,我们必须调用AudioManager.abandonAudioFocus(onAudioFocusChangeListener l)释放音频焦点。
8.0 之后实现
从Android 8.0开始(API 26),请求音频焦点的方式以及系统对音频焦点变化的管理有些微妙的变化。 首先,对音频焦点变化的管理的变化体现在两个方面,延迟获取焦点和自动降低音量。
延迟获取焦点
在Android 8.0之前,当我们请求音频焦点的时候,只会返回两种结果,要么请求成功(AUDIOFOCUS_REQUEST_GRANTED),要么请求失败(AUDIOFOCUS_REQUEST_FAILED)。
而从Android 8.0开始,还有一种结果,延迟成功请求(AUDIOFOCUS_REQUEST_DELAYED),这个也是成功的请求,但是这个请求具有延迟性。例如当我们处于通话状态的时候,我们很显然不希望任何app来获取到音频焦点来做些事,例如播放音乐。
然而只有设置过AudioFocusRequest.Builder.setAcceptsDelayedFocusGain(true)才能获取到这种结果,这个我们后面会讲到。那么我们怎么知道什么时候获取到了音频焦点呢,当然还需要设置AudioManager.OnAudioFocusChangeListener这个音频焦点变化的监听器,通过回调确认何时获取到了音频焦点。
自动降低音量
在Android 8.0之前,如果请求焦点使用了AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK参数,它表明希望拥有了音频焦点的其他应用降低音量来使用音频,然而并不是所有的应用都会这样做(有可能开发者忘记了优化),因为这并不是系统强制的。但是, 从Android 8.0开始,这个降低音量的工作,就是系统默认行为了,可以说是一个良心的优化。
如果我不希望系统自动给我降低音量,而是想自己暂停音频相关的工作,那咋办?这个可以通过AudioFocusRequest.Builder.setWillPauseWhenDucked(true)方法取消系统的默认行为,然后通过监听音频焦点变化
音频焦点请求方式
从 Android 8.0(API 级别 26)开始,当您调用 requestAudioFocus() 时,必须提供 AudioFocusRequest 参数。要释放音频焦点,请调用 abandonAudioFocusRequest() 方法,该方法也接受 AudioFocusRequest 作为参数。在请求和放弃焦点时,应使用相同的 AudioFocusRequest 实例。
要创建 AudioFocusRequest,请使用 AudioFocusRequest.Builder。由于焦点请求始终必须指定请求的类型,因此此类型会包含在构建器的构造函数中。使用构建器的方法来设置请求的其他字段:
- setFocusGain(): 只有这个方法是必须的,而传入的参数与8.0之前使用AudioManager.requestAudioFocus()传入的第三个参数一样,都是用来表示持续时间。
- setAudioAttributes(): 这个方法是用来描述app的使用情况。这方法需要传入一个AudioAttributes对象,这个对象也是使用Builder模式来构造,例如使用AudioAttributes.Builder.setUsage()来描述使用这个音频来干什么,我们可以传入一个AudioAttributes.USAGE_MEDIA来表明用这个音频来作为媒体文件来播放,也可以传入一个AudioAttributes.USAGE_ALARM来表明用这个来作为闹铃。
- setWillPauseWhenDucked(): 这个前面说过,是为了覆盖系统默认降低音量的行为,但是必须要设置AudioManager.OnAudioFocusChangeListener才能自己处理这类情况。
- setAcceptsDelayedFocusGain(): 这个前面也说过,这个是为了能够延迟获取到焦点的必须条件,但是同时也必须要设置AudioManager.OnAudioFocusChangeListener才能得知何时获取到焦点。
- setOnAudioFocusChangeListener(): 音频焦点变化监听器。值得一提的是这个方法有个重载的方法,有一个重载方法有两个参数,第二个参数为Handler对象,看到Handler应该明白了,是为了使用它的消息队列来顺序处理这个回调
响应音频焦点更改
当应用获得音频焦点后,它必须能够在其他应用为自己请求音频焦点时释放该焦点。出现这种情况时,您的应用会收到对 AudioFocusChangeListener 中的 onAudioFocusChange() 方法的调用,该方法是您在应用调用 requestAudioFocus() 时指定的。
传递给 onAudioFocusChange() 的 focusChange 参数表示所发生的更改类型。它对应于获取焦点的应用所使用的持续时间提示。您的应用应该做出适当的响应。
暂时性失去焦点
如果焦点更改是暂时性的(AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 或 AUDIOFOCUS_LOSS_TRANSIENT),您的应用应该降低音量(如果您不依赖于自动降低音量)或暂停播放,否则保持相同的状态。
在暂时性失去音频焦点时,您应该继续监控音频焦点的变化,并准备好在重新获得焦点后恢复正常播放。当抢占焦点的应用放弃焦点时,您会收到一个回调 (AUDIOFOCUS_GAIN)。此时,您可以将音量恢复到正常水平或重新开始播放。
永久性失去焦点
如果是永久性失去音频焦点 (AUDIOFOCUS_LOSS),则其他应用会播放音频。您的应用应立即暂停播放,因为它不会收到 AUDIOFOCUS_GAIN 回调。要重新开始播放,用户必须执行明确的操作,例如在通知或应用界面中按播放传输控件。
相关文章:
Android 音频焦点管理
前言 前面写过一篇类似的文章,没写完,今天再详细描述一下。 Android音频焦点申请处理 音频焦点管理的意义 两个或两个以上的 Android 应用可同时向同一输出流播放音频。系统会将所有音频流混合在一起。虽然这是一项出色的技术,但却会给用…...
大模型+自动驾驶
论文:https://arxiv.org/pdf/2401.08045.pdf 大型基础模型的兴起,它们基于广泛的数据集进行训练,正在彻底改变人工智能领域的面貌。例如SAM、DALL-E2和GPT-4这样的模型通过提取复杂的模式,并在不同任务中有效地执行,从…...
openssl3.2 - 测试程序的学习 - test\aesgcmtest.c
文章目录 openssl3.2 - 测试程序的学习 - test\aesgcmtest.c概述笔记能学到的流程性内容END openssl3.2 - 测试程序的学习 - test\aesgcmtest.c 概述 openssl3.2 - 测试程序的学习 aesgcmtest.c 工程搭建时, 发现没有提供 test_get_options(), cleanup_tests(), 需要自己补上…...
C语言——操作符详解2
目录 0.过渡0.1 不创建临时变量,交换两数0.2 求整数转成二进制后1的总数 1.单目表达式2. 逗号表达式3. 下标访问[ ]、函数调用( )3.1 下标访问[ ]3.2 函数调用( ) 4. 结构体成员访问操作符4.1 结构体4.1.1 结构体的申明4.1.2 结构体变量的定义和初始化 4.2 结构体成…...
(免费领源码)java#Springboot#mysql旅游景点订票系统68524-计算机毕业设计项目选题推荐
摘 要 科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作…...
帝国cms7.5 支付升级优化版文库范文自动生成word/PDF文档付费复制下载带支付系统会员中心整站模板源码sitemap百度推送+安装教程
帝国cms7.5 支付升级优化版文库范文自动生成word/PDF文档付费复制下载带支付系统会员中心整站模板源码sitemap百度推送+安装教程 (购买本专栏可免费下载栏目内所有资源不受限制,持续发布中,需要注意的是,本专栏为批量下载专用,并无法保证某款源码或者插件绝对可用,介意不…...
【node】关于npm、yarn、npx的区别与使用
文章目录 npm (Node Package Manager):安装依赖运行脚本 npx:执行项目依赖中的命令 yarn:安装依赖eg.使用npx yarn install 的作用 npm (Node Package Manager): 用途: npm 是 Node.js 官方提供的包管理工具,用于安装、管理和分享 JavaScript 代码包。安…...
力扣0099——恢复二叉搜索树
恢复二叉搜索树 难度:中等 题目描述 给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。 示例1 输入: root [1,3,null,null,2] 输出:[3,1,null,nul…...
机器学习核心算法
目录 逻辑回归 算法原理 决策树 决策树算法概述 树的组成 决策树的训练与测试 切分特征 衡量标准--熵 信息增益 决策树构造实例 连续值问题解决 预剪枝方法 分类与回归问题解决 决策树解决分类问题步骤 决策树解决回归问题步骤 决策树代码实例 集成算法 Baggi…...
libjsoncpp 的编译和交叉编译
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...
【Unity美术】如何用3DsMax做一个水桶模型
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:Uni…...
如何用一根网线和51单片机做简单门禁[带破解器]
仓库:https://github.com/MartinxMax/Simple_Door 支持原创是您给我的最大动力… 原理 -基础设备代码程序- -Arduino爆破器程序 or 51爆破器程序- 任意选一个都可以用… —Arduino带TFT屏幕——— —51带LCD1602——— 基础设备的最大密码长度是0x7F,因为有一位…...
在 VUE 项目中,使用 Axios 请求数据时,提示跨域,该怎么解决?
在 VUE 项目开发时,遇到个问题,正常设置使用 Axios 库请求数据时,报错提示跨域问题。 那在生产坏境下,该去怎么解决呢? 其可以通过以下几种方式去尝试解决: 1、设置允许跨域请求的响应头 1.1 在响应头中…...
1.【Vue3】前端开发引入、Vue 简介
1. 前端开发引入 1.1 前端开发前置知识 通过之前的学习,已经通过 SpringBoot 和一些三方技术完成了大事件项目的后端开发。接下来开始学习大事件项目的前端开发,前端部分借助两个框架实现: Vue3(一个 JS 框架)基于 …...
一起学习ETCD系列——运维操作之etcdctl使用
文章目录 概要一、命令二、实操2.1、基本操作2.2、watch2.3、租约2.4、分布式锁2.5、角色2.6、用户2.7、认证2.8、集群 概要 本文主要用来总结ETCD客户端ctcdctl的命令操作,在运维过程中可能常常用到的。 一、命令 etcd工具 etcdctl官方命令示例 [roottest etcd…...
Spring Security 存储密码之 JDBC
Spring Security的JdbcDaoImpl实现了UserDetailsService接口,通过使用JDBC提供支持基于用户名和密码的身份验证。 JdbcUserDetailsManager扩展了JdbcDaoImpl,通过UserDetailsManager接口提供UserDetails的管理功能。 当Spring Security配置为接受用户名/密码进行身份验证时,…...
第3章-python深度学习——(波斯美女)
第3章 神经网络入门 本章包括以下内容: 神经网络的核心组件 Keras 简介 建立深度学习工作站 使用神经网络解决基本的分类问题与回归问题 本章的目的是让你开始用神经网络来解决实际问题。你将进一步巩固在第 2 章第一个示例中学到的知识,还会将学到的…...
蓝桥杯备战——4.继电器/蜂鸣器
1.分析原理图 最好自己先去查查138以及ULN2003的使用方法,我这里直接讲思路。 由上图我们可以看到如果138输入ABC101,则输出Y50,此时若WR通过跳线帽接地则Y5C1 ,于是573(U9)处于输出跟随输入P0状态,此时若P061,则573输出Q71&am…...
Redis高级特性之地理空间索引
Redis的地理空间索引是一种功能强大的工具,用于存储和查询地理空间数据。这个特性主要通过Redis的地理空间数据类型 - GeoSet(地理集合)来实现。在这篇文章中,我们将探索Redis地理空间数据类型的使用和应用。 1. Redis GeoSet 简…...
R语言【taxlist】——as():将 taxlist 对象强制转换为 list 对象
Package taxlist version 0.2.4 Description 可以应用 S4 对象到 list 对象的强制转换来探索它们的内容,避免由它们的验证引起的错误。 Usage S4_to_list(x) Argument 参数【x】:一个 taxlist 类对象或任意 S4 类。 Details 将 taxlist 对象强制转换…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
