AudioFocus是Android引入的一个Audio协调机制,当多方需要使用Audio资源时,可以通过AudioFocus机制来协调配合,提高用户的体验。    该机制需要开发者主动去遵守,比如A应用没遵守该机制,则其它遵守了该机制的应用是完全没办法影响A应用的。    试想下后台在播放着音乐的时候你点开了某个视频,使得后台的音乐和视频的声音一起播放,毫无关联的声音一同播放会给用户带来极差的体验,此时我们就可以通过AudioFocus机制来解决这样的问题。

使用AudioFocus机制主要是通过android.media.AudioManager这个类来进行的 public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) 方法请求获取焦点,

如果获取成功,会返回int值AudioManager.AUDIOFOCUS_REQUEST_GRANTED,

失败则返回AudioManager.AUDIOFOCUS_REQUEST_FAILED。

通过abandonAudioFocus(OnAudioFocusChangeListener l)方法放弃焦点。 由于音频焦点是唯一的,所以可以在需要播放音乐时去申请音频焦点,如果获取到了则播放,同时正在播放的音频在失去音频焦点时停止播放或者调低音量,从而达到音频播放间的相互协调。

对requestAudioFocus方法的参数进行解析:  OnAudioFocusChangeListener是一个接口,在这个接口里面只有一个方法需要实现 public void onAudioFocusChange(int focusChange);

该方法会在焦点状态变化的时候被调用 参数focusChange代表变化后当前的状态,一共有以下四个值:

AUDIOFOCUS_GAIN 重新获取到音频焦点时触发的状态。

AUDIOFOCUS_LOSS 失去音频焦点时触发的状态,且该状态应该会长期保持,此时应当暂停音频并释放音频相关的资源。

AUDIOFOCUS_LOSS_TRANSIENT 失去音频焦点时触发的状态,但是该状态不会长时间保持,此时应该暂停音频,且当重新获取音频焦点的时候继续播放。

AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 失去音频焦点时触发的状态,在该状态的时候不需要暂停音频,但是应该降低音频的声音。

streamType(STREAM_RING,STREAM_MUSIC,STREAM_ALARM,STREAM_NOTIFICATION,STREAM_BLUETOOTH_SCO,STREAM_DTMF,STREAM_TTS)

durationHint用来表示获取焦点的时长,同时通知其它获取了音频焦点的OnAudioFocusChangeListener该如何相互配合,有以下几个值: AUDIOFOCUS_GAIN 代表此次申请的音频焦点需要长时间持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS。

AUDIOFOCUS_GAIN_TRANSIENT 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:适用于短暂的音频,在接收到事件通知等情景时可使用该durationHint。

AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:在需要录音或语音识别等情景时可使用该durationHint。

AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 代表此次申请不需要暂停其它申请的音频播放,应用跟其他应用共用焦点但播放的时候其他音频会降低音量。原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK。

在AudioManager的requestAudioFocus调用内部重载后的方法,根据传进来的streamType,构造了一个AudioAttributes对象向下传递,这个AudioAttributes主要是存储了一些音频流信息的属性. 这里面对falgs进行了与操作,由于之前传进来的是0,所以转换后的结果还是0.

继续AudioManager.requestAudioFocus中 public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) 里面首先在registerAudioFocusRequest中注册所有的

AudioFocusRequest private final ConcurrentHashMap<String, FocusRequestInfo> mAudioFocusIdListenerMap =         new ConcurrentHashMap<String, FocusRequestInfo>();

final FocusRequestInfo fri = new FocusRequestInfo(afr, (h == null) ? null :new ServiceEventHandlerDelegate(h).getHandler());

其中mAudioFocusIdListenerMap根据AudioFocusRequest中OnAudioFocusChangeListener对象的来生成一个key,value则为根据afr生成的FocusRequestInfo ,存储在了mAudioFocusIdListenerMap对象中。 而之前所述的abandonAudioFocus中有unregisterAudioFocusRequest做的操作就是remove掉mAudioFocusIdListenerMap中的OnAudioFocusChangeListener

在AudioManager中还有一个电话相关的调用:

public void requestAudioFocusForCall(int streamType, int durationHint)

这个调用的也是AudioService中的requestAudioFocus并且往其中设置了一个clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID的状态。 继续往下就是进入到AudioService.requestAudioFocus中首先会进行权限检查,这里面就用到了AudioSystem.IN_VOICE_COMM_FOCUS_ID 也就是说如果clientId等于AudioSystem.IN_VOICE_COMM_FOCUS_ID,且要申请到MODIFY_PHONE_STATE的权限,否则会申请焦点失败。

AudioService也只是做了中转,并没有做实际的操作,具体实现都是在MediaFocusControl.requestAudioFocus中大致过程如下:

private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); 最主要的是对mFocusStack栈的操作,用来维护各client的申请和释放。

1.判断mFocusStack.size() 不能超过100

2.检查当前栈顶的元素是否是Phone应用占用,如果Phone处于占用状态,那么focusGrantDelayed = true。

3. 压栈之前,需要检查当前栈中是否已经有这个应用的记录,如果有的话就删除掉。

如果mFocusStack不为空,并且栈顶的clientId与要申请焦点的clientId相同,得到栈顶元素即FocusRequester对象。如果申请的时长和flags都相同,则表示重复申请,直接返回成功,如果如果申请的时长和flags有一个不相同,则认为需要重新申请,此时如果focusGrantDelayed = false则需要将栈顶的元素出栈并将其释放

4. // focus requester might already be somewhere below in the stack, remove it

removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);

移除可能在栈中(栈顶或者栈中)其他位置存在着相同clientId的元素

a:如果要释放的应用是在栈顶,则释放之后,还需要通知先在栈顶应用,其获得了audiofocus;

b:如果要释放的应用不是在栈顶,则只是移除这个记录,不需要更改当前audiofocus的占有情况。

5.创建FocusRequester实例将请求包含的各种信息传入

AudioAttributes aa, int focusChangeHint, IBinder cb,  IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, int sdk

6.如果focusGrantDelayed = true,那么就会延迟申请,并把此次请求FocusRequester实例入栈,但是此时记录不是被压在栈顶,而是放在lastLockedFocusOwnerIndex这个位置,也就是打电话这个记录的后面;如果focusGrantDelayed = false即不需要延迟获得焦点,同样创建FocusRequester实例,但是先要通知栈里其他记录失去焦点,然后压入栈顶,最后通知自己获得焦点成功

遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法 通知栈中其他元素丢失焦点流程. stackIterator.next()得到的是FocusRequester对象,因此查看FocusRequester中handleExternalFocusGain的代码

主要关注两个变量gainRequest和mFocusLossReceived, mFocusLossReceived这个值在handleFocusLoss中进行赋值的,默认值是AudioManager.AUDIOFOCUS_NONE。

* gainRequest这个是传进来的,例如

AudioManager.AUDIOFOCUS_GAIN

* return AudioManager.AUDIOFOCUS_LOSS

* 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT

* 则return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT

在handleFocusLoss中

fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);

通过mFocusDispatcher对象调用了dispatchAudioFocusChange方法,将mFocusLossReceived和mClientId传了进去。

3.1 FocusRequester构造方法的第四个参数IAudioFocusDispatcher afl

3.2 MediaFocusControl的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd。

3.3 AudioService的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd 最终在AudioManager中实现。IAudioFocusDispatcher 的回调dispatchAudioFocusChange方法。

在dispatchAudioFocusChange中通过Handler发送MSSG_FOCUS_CHANGE

在ServiceEventHandlerDelegate 的创建handleMessage在其中根据MSSG_FOCUS_CHANGE来回调,而msg.arg1为focusChange即mFocusLossReceived

listener.onAudioFocusChange(msg.arg1);

最终app中实现onAudioFocusChange根据焦点的切换做相应的控制。

Audio AudioFocus流程相关推荐

  1. Android音频子系统(十)------MTK Audio录音流程代码解析

    你好!这里是风筝的博客, 欢迎和我一起交流. Android framework中的代码每个平台基本都是大同小异,只有Hal上代码才是厂商特制,每个平台都不相同,这里以MTK平台为例,记录下MTK平台 ...

  2. Android7.1 audio 播放流程(三十五)

    android audio 生产者与消费者 简介 全面接触生产者/消费者问题是在操作系统原理中,并发性原理讨论的问题 生产者/消费者问题.最近的工作偏向音频,接着上一篇文章,用生产者,消费者模型来理解 ...

  3. Android usb audio调用流程(二)

    android audio 生产者与消费者 简介 全面接触生产者/消费者问题是在操作系统原理中,并发性原理讨论的问题 生产者/消费者问题.最近的工作偏向音频,接着上一篇文章,用生产者,消费者模型来理解 ...

  4. Anbox之server端audio播放流程(十二)

    Anbox是一个免费的开源兼容层,旨在允许为Android开发的移动应用程序和移动游戏在GNU / Linux发行版上运行. 使用LXC执行Android运行时环境,将Android的目录结构重新创建 ...

  5. Android Audio播放流程详解

    本文涉及的所有代码都是AOSP源码 目录 1. AudioTrack 2.创建AudioTrack对象 1. AudioTrack AudioTrack用于播放PCM流格式的音频数据.播放器会在fra ...

  6. Audio Focus分析总结

    1.深入理解Android 卷3 张大伟. 讲的是Android M的audiofocus,非常精辟,就是版本有些老. 和张大伟差不多,an 7.1: cshandroid音频系统(5):AudioS ...

  7. Android Media (Audio) Framework 多媒体系统框架

    http://blog.csdn.net/lskshz/article/details/17264113 原址:http://blog.csdn.net/myzhzygh/article/detail ...

  8. [RK3288][Android6.0] StageFright解码流程小结

    原址 Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92以Gallery播放视频为例(只关注MediaPlayer相关部分): rk用的是ffplayer ...

  9. [Android6.0] StageFright解码流程小结

    Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 以Gallery播放视频为例(只关注MediaPlayer相关部分): rk用的是ffplayer,但 ...

  10. webaudio ajax,Web Audio 入门之读取左右声道数据

    说到音频,大家应该立刻会想到Audio,和Audio标签完全不同,Web Audio的功能更为强大.Audio 和 Web Audio的关系,就像img和canvas的关系一样. Web Audio ...

最新文章

  1. CentOS下首次使用as86汇编器
  2. 灰色简约三级CSS下拉菜单代码
  3. python 2 与 python 3 —— 转义及编码(\u, \x)
  4. 牛逼哄哄的 RabbitMQ 到底有啥用?
  5. 饱和气压与温度的关系_饱和水蒸气压计算公式,看懂的赶紧来
  6. 立即从iOS 10和macOS Sierra下载壁纸
  7. python获取英文字母、英文标点符号、中文标点符号
  8. Pytorch3D环境血泪安装史
  9. 学习笔记:使用requests+Beautiful4爬取优美图库
  10. 人像大片这么拍才像样:OPPO R15加入3D人像打光
  11. Thinkpad T460更换固态硬盘
  12. matlab2014 锯齿波,matlab周期锯齿波
  13. 邮件多面手!Foxmail 6.5正式版新体验
  14. ThinkPad R480 C盘 固态128G 升级到 512G + 系统无缝迁移,不用重装软件
  15. 校招 | 华为终端云服务招聘NLP、CV、推荐和大数据等岗位
  16. 2023年电气,电子与信息工程国际会议(ISEEIE-2023)
  17. 西门子S7-200 SMART控制步进电机(二)
  18. Python实用秘技,复杂zip文件的解压
  19. RAD Studio 10.2.3 Tokyo v25.0.31059.3231 ISO镜像官方下载
  20. 浙江教育计算机网官网首页,浙江省教育计算机网:建设高速互联的地区教科网...

热门文章

  1. Android中的占位符
  2. Jquery插件实现“点击获取验证码后60秒内禁止重新获取(防刷新)”
  3. 虚幻引擎(UE4) UMG 创建菜单
  4. nas android 开源,五大开源NAS项目盘点
  5. HDU 4826Labyrinth(dp)
  6. 金融直播营销,主播必须这样做!
  7. hyd.modify
  8. Android 取消蓝牙消息通知流程分析(一)
  9. 开源项目推荐 | 面向智慧城市的计算机视觉算法基准测试 Benchmark for Smart City上线
  10. 高斯帕滑翔机枪matlab,高斯帕滑翔机枪的播种机这么做?