Android是多任务系统,Audio系统是竞争资源。Android2.2之前,没有内建的机制来解决多个程序竞争Audio的问题,2.2引入了称作AudioFocus的机制来管理对Audio资源的竞争的管理与协调。本文主要讲解AudioFocus的使用。

按照AudioFocus的机制,在使用AudioStream之前,需要申请AudioFocus,在获得AudioFocus之后才可以使用相应的AudioStream;如果有别的程序竞争你正在使用的AudioStream,你的程序需要在收到通知之后做停止播放或者降低声音的处理。值得指出的是,这种机制是需要合作完成的,需要所有使用Audio资源的程序都按照这种机制来做,而如果有程序在它失去AudioFocus的时候仍然在使用Audio,AudioFocus拿它也没办法。而这一点对于开放系统的Android来说很致命的:用户可能安装没遵守这种机制的程序,或者版本太老还没引入这种机制的程序,这最终会导致很差的用户体验。

对于手机方案公司来说,要做的能做的事情就是教育和培训团队成员以保证自己内建的程序遵守机制没问题,这包括了Android原生的程序、自己开发的程序,以及适配第三方的程序。

一、AudioFocus的申请与释放

下面看与AudioFocus的相关的类:

获取/放弃AudioFocus的方法都在android.media.AudioManager中,获取AudioFocus用requestAudioFocus();用完之后,放弃AudioFocus,用abandonAudioFocus()

其中,参数

  • streamType是《Android中的Audio播放:音量和远程播放控制》中说明的AudioStream,其值取决于AudioManager中的STREAM_xxx;
  • durationHint是持续性的指示:
    • AUDIOFOCUS_GAIN指示申请得到的Audio Focus不知道会持续多久,一般是长期占有;
    • AUDIOFOCUS_GAIN_TRANSIENT指示要申请的AudioFocus是暂时性的,会很快用完释放的;
    • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK不但说要申请的AudioFocus是暂时性的,还指示当前正在使用AudioFocus的可以继续播放,只是要“duck”一下(降低音量)。
  • AudioManager.OnAudioFocusChangeListener是申请成功之后监听AudioFocus使用情况的Listener,后续如果有别的程序要竞争AudioFocus,都是通过这个Listener的onAudioFocusChange()方法来通知这个Audio Focus的使用者的。

返回值,可能是:

  • AUDIOFOCUS_REQUEST_GRANTED:申请成功;
  • AUDIOFOCUS_REQUEST_FAILED:申请失败。

二、AudioFocus被抢占与重新获得

由上节中知道,申请/释放AudioFocus时传入了AudioManager.OnAudioFocusChangeListener这个参数,其onAudioFocusChange()方法是Audio Focus被抢占与再次获得通知的地方。所以,每个要使用AudioFocus的程序都要小心实现这个函数,保证AudioFocus实现的一致性。

onAudioFocusChange()方法的focusChange参数指示了该AudioFocus的竞争者对AudioFocus的拥有情况,取值如下:

  • AUDIOFOCUS_GAIN:获得了Audio Focus;
  • AUDIOFOCUS_LOSS:失去了Audio Focus,并将会持续很长的时间。这里因为可能会停掉很长时间,所以不仅仅要停止Audio的播放,最好直接释放掉Media资源。而因为停止播放Audio的时间会很长,如果程序因为这个原因而失去AudioFocus,最好不要让它再次自动获得AudioFocus而继续播放,不然突然冒出来的声音会让用户感觉莫名其妙,感受很不好。这里直接放弃AudioFocus,当然也不用再侦听远程播放控制【如下面代码的处理】。要再次播放,除非用户再在界面上点击开始播放,才重新初始化Media,进行播放。
  • AUDIOFOCUS_LOSS_TRANSIENT:暂时失去Audio Focus,并会很快再次获得。必须停止Audio的播放,但是因为可能会很快再次获得AudioFocus,这里可以不释放Media资源;
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时失去AudioFocus,但是可以继续播放,不过要在降低音量。

下面是onAudioFocusChange()方法处理的代码片段:

  1. OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
  2. public void onAudioFocusChange(int focusChange) {
  3. if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
  4. // Pause playback
  5. else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
  6. am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
  7. am.abandonAudioFocus(afChangeListener);
  8. // Stop playback
  9. else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
  10. // Lower the volume
  11. else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
  12. // Resume playback or Raise it back to normal
  13. }
  14. }
  15. };

三、典型的应用AudioFocus的场景

下面的时序图描述了AudioFocus被抢占与再次获取的典型场景:

Audio Focus被抢占与再次获取的时序图

注意为了描述简单,此图中除了两个竞争Audio Focus的App之外,只用AudioManager表征了Android的AudioFocus机制中内部参与的对象,实际AudioManager只是外部的表象,内部参与的对象很多,回调函数也并非简单的直接由AudioManager调用,其中还包含了复杂的IPC机制。

图中:

  • AudioFocus Client通过requestAudioFocus()获取AudioFocus,在获得AudioFocus之后,开始播放Audio[Step#1 ~ #2];
  • 其它程序(Other App)也通过requestAudioFocus()获取同类AudioStream的AudioFocus [Step#3]
  • AudioFocus Client失去了Audio Focus,在onAudioFocusChanged()中,根据focusChange的值,做第二节中的处理[Step#4];
  • 其它程序(Other App)获取Audio Focus之后,开始播放Audio[Step#5];
  • 其它程序(Other App)使用Audio之后,通过abandonAudioFocus()归还AudioFocus [Step#6];
  • AudioFocus Client重新获得了Audio Focus,可做进一步的处理 [Step#7]

小结

Audio Focus机制要参与各方充分理解并统一遵照施行,有没有遵照者或者实现有误的程序存在就可能打破这一机制,带来糟糕的用户体验。在保证Built-in程序没问题的前提下,如果进入AndroidMarket之前的程序都严格执行了AudioFocus相关的测试,应该也没问题。

Android开发之Audio播放:竞争Audio之Audio Focus的应用相关推荐

  1. android语音播放工具类,Android开发之MediaPlayer多媒体(音频,视频)播放工具类

    本文实例讲述了Android开发之MediaPlayer多媒体(音频,视频)播放工具类.分享给大家供大家参考,具体如下: package com.android.imooc.chat; import ...

  2. Android开发之旅:组件生命周期(二)

    引言 应用程序组件有一个生命周期--一开始Android实例化他们响应意图,直到结束实例被销毁.在这期间,他们有时候处于激活状态,有时候处于非激活状态:对于活动,对用户有时候可见,有时候不可见.组件生 ...

  3. Android开发之旅:应用程序基础及组件

    --成功属于耐得住寂寞的人,接下来几篇将讲述Android应用程序的原理及术语,可能会比较枯燥.如果能够静下心来看,相信成功将属于你. 引言 为了后面的例子做准备,本篇及接下来几篇将介绍Android ...

  4. Android开发之旅:android架构

    引言 通过前面两篇: Android 开发之旅:环境搭建及HelloWorld Android 开发之旅:HelloWorld项目的目录结构 我 们对android有了个大致的了解,知道如何搭建and ...

  5. android中oncreate方法,android开发之onCreate( )方法详解

    这里我们只关注一句话:This is where you should do all of your normal static set up.其中我们只关注normal static, normal ...

  6. android开发之Intent.setFlags()_让Android点击通知栏信息后返回正在运行的程序

    android开发之Intent.setFlags()_让Android点击通知栏信息后返回正在运行的程序   在应用里使用了后台服务,并且在通知栏推送了消息,希望点击这个消息回到activity, ...

  7. Android开发之TextView高级应用

    Android开发之TextView高级应用 我们平时使用TextView往往让它作为一个显示文字的容器,但TextView的功能并不局限于此.以下就和大家分享一下TextView的一些使用技巧. A ...

  8. android之json解析优化,Android开发之json解析

    目前正在尝试着写app,发现看懂代码和能写出来差距很大,最关键的是java基础比较的差,因为只会python,java基础只学习了一个礼拜就过了.感觉java写出来的代码不如python简单明了. 上 ...

  9. android注册弹窗,Android开发之PopupWindow创建弹窗、对话框的方法详解

    本文实例讲述了Android开发之PopupWindow创建弹窗.对话框的方法.分享给大家供大家参考,具体如下: 简介: PopupWindow 可创建类似对话框风格的窗口 效果: 使用方法: 使用P ...

  10. Android开发之SpannableString具体解释

    在实际的应用开发过程中常常会遇到.在文本的不同部分显示一些不同的字体风格的信息如:文本的字体.大小.颜色.样式.以及超级链接等. 普通情况下,TextView中的文本都是一个样式.对于类似的情况.能够 ...

最新文章

  1. 最大后验概率估计算法
  2. SQL Server-聚焦UNIOL ALL/UNION查询(二十三)
  3. 月活675万 三翼鸟以三大能力“重建”行业赛道
  4. Python实现网页截图
  5. 《剑指offer》栈的压入、弹出序列
  6. 如何从Angular的ActivatedRoute数据结构中获得运行时路由信息
  7. MFC中OnCtlColor的用法(改变控件颜色)
  8. 小朋友学C++(1)
  9. Linux内核逻辑结构
  10. 首款搭载鸿蒙os的设备,华为发布会配件汇总,首款搭载 鸿蒙OS 的设备来了
  11. FreeWheel容力:如何打造更高质效的技术团队
  12. xpath的使用-通过xpath_helper进行的演练
  13. Javascript笔记:(实践篇)从jQuery插件技术说起-分析extend方法的源码(发现extend方法里有bug)(下篇)...
  14. F-Stack:ff_run函数详解
  15. python权威指南 pdf_Python高级编程(第2版) 中文完整pdf扫描版[76MB]
  16. Android本地应用程序应用方式介绍
  17. 马尔科夫链(Markov Chain),机器学习和人工智能的基石
  18. [架构之路-42]:目标系统 - 系统软件 - Linux下的网络通信-2-无线局域网WIFI原理、WIFI与3G/4G/以太网/蓝牙的协议转换
  19. 计算机网络应用答题卡,计算机软考中高级答题卡填涂注意事项
  20. 笔记本创建wifi热点

热门文章

  1. 图像处理——相位恢复(GS,TIE,改进型角谱迭代法)(已更新代码)
  2. ZCash零知识证明
  3. ASCII阿斯克码对照表
  4. 水果库存系统(基础版)
  5. 10以内的分解与组成怎么教_“10以内数的组成”训练方法
  6. java其他框架杂记
  7. 怎么计算一个图像的面积呢?????
  8. Elasticsearch之路由
  9. 有没有可以测试充电宝电流电压的软件,USB充电电流/电压检测仪USB电流和电压测试仪移动电源测试...
  10. 【原创】Ubuntu 下使用 NCverilog 仿真 Verilog 工程