Android音频焦点管理实例详解

目录
  • 音频焦点管理的意义
  • 音频焦点管理的行为准则
  • 版本兼容
  • 在Android 8.0(API 26) 之前对音频焦点具体处理实现
  • 8.0 之后实现
    • 延迟获取焦点
    • 自动降低音量
    • 音频焦点请求方式
    • 响应音频焦点更改
      • 暂时性失去焦点
      • 永久性失去焦点
  • 附音频基础知识
  • 总结

音频焦点管理的意义

两个或两个以上的 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参数,它表明希望拥有了音频焦点的其他应用降低音量来使用音频,然而并不是所有的应用都会这样做(有可能开发者忘记了优化),因为这并不是系统强制的。But, 从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 回调。要重新开始播放,用户必须执行明确的操作,例如在通知或应用界面中按播放传输控件。

附音频基础知识

采样和采样频率:一秒钟内采样的次数称为采样频率。采样频率越高,越接近原始信号,但是也加大了运算处理的复杂度。根据Nyquist采样定理,要想重建原始信号,采样频率必须大于信号中最高频率的两倍。人能感受到的频率范围为20HZ--20kHZ, 一般音乐的采样频率为44.1kHZ, 更高的可以是48kHZ和96kHZ,不过一般人用耳听感觉不出差别了。语音主要是以沟通为主,不需要像音乐那样清晰,用16k采样的语音就称为高清语音了。现在主流的语音采样频率为16kHz。

采样位数:数字信号是用0和1来表示的。采样位数就是采样值用多少位0和1来表示,也叫采样精度,用的位数越多就越接近真实声音。如用8位表示,采样值取值范围就是-128--127,如用16位表示,采样值取值范围就是-32768--32767。现在一般都用16位采样位数。

声道:声道是指处理的声音是单声道还是立体声。Android支持双声道立体声和单声道。CHANNEL_IN_MONO单声道,CHANNEL_IN_STEREO立体声。单声道在声音处理过程中只有单数据流,而立体声则需要左、右声道的两个数据流。显然,立体声的效果要好,但相应的数据量要比单声道的数据量加倍。

码率:就是比特率。比特率是指每秒传送的比特(bit)数。码率=采样率X采样位数X声道数。

音频采集和播放:一般用专门的芯片(通常叫codec芯片)采集音频,做AD转换,然后把数字信号通过I2S总线(主流用I2S总线,也可以用其他总线,比如PCM总线)送给CPU处理(也有的会把codec芯片与CPU芯片集成在一块芯片中)。当要播放时CPU会把音频数字信号通过I2S总线送给codec芯片,然后做DA转换得到模拟信号再播放出来。

总结

到此这篇关于Android音频焦点管理的文章就介绍到这了,更多相关Android音频焦点管理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android音频可视化开发案例说明

    Android 调用自带的录制音频程序 Android中有自带的音频录制程序,我们可以通过指定一个Action MediaStore.Audio.Media.RECORD_SOUND_ACTION的Intent来 启动它就可以了.然后在onActivityResult()方法中,获取Intent的Data,就是录制的音频对应的URI. java代码: 复制代码 代码如下: package eoe.demo; import android.app.Activity; import android.

  • Android添加音频的几种方法

    在res文件夹中新建一个文件夹,命名为raw.在里面放入我们需要的音频文件. 第一种: // 根据资源创建播放器对象 player = MediaPlayer.create(this, R.raw.xiaoxiaole); try { player.prepare();// 同步 } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOExcept

  • 详解Android开发录音和播放音频的步骤(动态获取权限)

    步骤: 配置权限: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.work.mediaplay"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission> <us

  • Android音频焦点管理实例详解

    目录 音频焦点管理的意义 音频焦点管理的行为准则 版本兼容 在Android 8.0(API 26) 之前对音频焦点具体处理实现 8.0 之后实现 延迟获取焦点 自动降低音量 音频焦点请求方式 响应音频焦点更改 暂时性失去焦点 永久性失去焦点 附音频基础知识 总结 音频焦点管理的意义 两个或两个以上的 Android 应用可同时向同一输出流播放音频.系统会将所有音频流混合在一起.虽然这是一项出色的技术,但却会给用户带来很大的困扰.为了避免所有音乐应用同时播放,Android 引入了“音频焦点”的

  • Android 十六进制状态管理实例详解

    目录 背景 示例 实现思路 代码测试 十六进制 总结 背景 最近需要实现一个状态管理类: 在多种场景下,控制一系列的按钮是否可操作. 不同场景下,在按钮不可操作的时候,点击弹出对应的Toast. 随着场景数量的增加,这个管理类的实现,就可能会越来越复杂. 刚好看到大佬的文章,顺便学习和实践一下.参考学习:就算不去火星种土豆,也请务必掌握的 Android 状态管理最佳实践 示例 还是用大佬那个例子. 例如,存在 3 种模式,和 3个按钮,按钮不可用的时候弹出对应的 Toast. 模式 A 下,要

  • Android activity堆栈及管理实例详解

    本示例演示如何通过设置Intent对象的标记,来改变当前任务堆栈中既存的Activity的顺序. 1. Intent对象的Activity启动标记说明: FLAG_ACTIVITY_BROUGHT_TO_FRONT 应用程序代码中通常不设置这个标记,而是由系统给单任务启动模式的Activity的设置. FLAG_ACTIVITY_CLEAR_TASK 如果给Intent对象添加了这个标记,那么在Activity被启动之前,会导致跟这个Activity关联的任何既存的任务都被清除.也就是说新的Ac

  • Android的搜索框架实例详解

    基础知识 Android的搜索框架将代您管理的搜索对话框,您不需要自己去开发一个搜索框,不需要担心要把搜索框放什么位置,也不需要担心搜索框影响您当前的界面.所有的这些工作都由SearchManager类来为您处理(以下简称"搜索管理器"),它管理的Android搜索对话框的整个生命周期,并执行您的应用程序将发送的搜索请求,返回相应的搜索关键字. 当用户执行一个搜索,搜索管理器将使用一个专门的Intent把搜索查询的关键字传给您在配置文件中配置的处理搜索结果的Activity.从本质上讲

  • Android原生音量控制实例详解

    本文主要涉及AudioService.还是基于5.1.1版本的代码. AudioService.java文件位于/framework/base/media/java/android/media/下. 音量控制是AudioService最重要的功能之一.先总结一下: AudioService音量管理的核心是VolumeStreamState.它保存了一个流类型所有的音量信息. VolumeStreamState保存了运行时的音量信息,而音量的生效则是在底层AudioFlinger完成的.所以进行音

  • Android 中ContentProvider的实例详解

    Android 中ContentProvider的实例详解 Content Provider 的简单介绍: * Android中的Content Provider 机制可支持在多个应用中存储和读取数据.这也是跨应用 共享数据的唯一方式.在Android系统中,没有一个公共的内存区域,供多个应用共享存储数据: * Android 提供了一些主要数据类型的ContentProvider ,比如:音频.视频.图片和私人通讯录等: 在android.provider 包下面找到一些android提供的C

  • Android 中 ActivityLifecycleCallbacks的实例详解

    Android 中 ActivityLifecycleCallbacks的实例详解           以上就是使用ActivityLifecycleCallbacks的实例,代码注释写的很清楚大家可以参考下, MyApplication如下: package com.cc; import java.util.LinkedList; import android.app.Activity; import android.app.Application; import android.os.Bun

  • Android 帧动画的实例详解

    Android 帧动画的实例详解 对于 Android 帧动画 大体上可以理解成 一张张图片 按一定顺序切换, 这样当连续几张图是一组动画时,就可以连起来了看成是一个小电影,你懂得 好得,比就装到这里,下面开始进入正题,由于产品需求 需要做一个 声音喇叭动态切换的样式,我特么第一就想到是帧动画切换,然后就百度了一些资料,发现 真的, 现在这个网上太多的资料是 copy粘贴过来的, 一错全错,对于这种情况我只想说,made,一群垃圾, 所以今天我将带你们走进Android 正确帧动画地址. 第一步

  • Android Intent封装的实例详解

    Android Intent封装的实例详解 什么是Intent: Intent是协调应用间.组件之间的通讯和交互.通过Intent你可以启动Activity.Service.Broadcasts.更可以跨程序调用第三方组件.例如:启动拨打电话界面.音乐播放等. 组件     启动 Activity startActicity() Service startService(),bindService( ) Broadcasts sendBroadcast() 使用Intent: 栗子:在一个Act

  • Android 完全退出的实例详解

    Android 完全退出的实例详解 首先,在基类BaseActivity里,注册RxBus监听: public class BaseActivity extends AppCompatActivity { Subscription mSubscription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Utils.intiSySBar(thi

随机推荐