Android中的Audio播放:竞争Audio之Audio Focus的应用
from://http://blog.csdn.net/thl789/article/details/7422931
Android是多任务系统,Audio系统是竞争资源。Android2.2之前,没有内建的机制来解决多个程序竞争Audio的问题,2.2引入了称作AudioFocus的机制来管理对Audio资源的竞争的管理与协调。本文主要讲解AudioFocus的使用。
按照AudioFocus的机制,在使用Audio之前,需要申请AudioFocus,在获得AudioFocus之后才可以使用Audio;如果有别的程序竞争你正在使用的Audio,你的程序需要在收到通知之后做停止播放或者降低声音的处理。值得指出的是,这种机制是需要合作完成的,需要所有使用Audio资源的程序都按照这种机制来做,而如果有程序在它失去AudioFocus的时候仍然在使用Audio,AudioFocus拿它也没办法。而这一点对于开放系统的Android来说很致命的:用户可能安装没遵守这种机制的程序,或者版本太老还没引入这种机制的程序,这最终会导致很差的用户体验。
对于手机方案公司来说,要做的能做的事情就是教育和培训团队成员以保证自己内建的程序遵守机制没问题,这包括了Android原生的程序、自己开发的程序,以及适配第三方的程序。
一、AudioFocus的申请与释放
下面看与AudioFocus的相关的类:

获取/放弃AudioFocus的方法都在android.media.AudioManager中,获取AudioFocus用requestAudioFocus();用完之后,放弃AudioFocus,用abandonAudioFocus()。
其中,参数:
- streamType是《Android中的Audio播放:音量和远程播放控制》中说明的AudioStream,其值取决于AudioManager中的STREAM_xxx,在AudioStream的裁决机制中并未有什么实际意义;
- 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()方法处理的代码片段:
- OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
- public void onAudioFocusChange(int focusChange) {
- if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
- // Pause playback
- } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
- am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
- am.abandonAudioFocus(afChangeListener);
- // Stop playback
- } else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
- // Lower the volume
- } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
- // Resume playback or Raise it back to normal
- }
- }
- };
三、典型的应用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()获取AudioFocus [Step#3]
- AudioFocus Client失去了Audio Focus,在onAudioFocusChanged()中,根据focusChange【focusChange的值与Other App申请时的durationHint相反,即focusChange = -1*durationHint】的值,做第二节中所描述的处理[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相关的测试,应该也没问题。
使用Audio的程序要做到:
- 使用前,用requestAudioFocus()申请AudioFocus,并根据应用的实际选取恰当的durationHint值;
- 正确的在AudioManager.OnAudioFocusChangeListener中响应AudioFocus失去和重新获取事件;
- Audio使用结束,用
abandonAudioFocus()归还AudioFocus。
问题点以及进一步的探讨
- 内部裁决机制怎样的?
- 申请的不同Audio Stream之间是不存在竞争的吗?
【更新2012-04-05】
1. AudioFocus中虽然把AudioStream作为参数,但是AudioFocus的内部裁决机制并未针对AudioStream做什么特别的处理。AudioFocus的处理针对所有的申请者来说的,除了它自身内部作为Alert的申请者有点特殊外,其它一律平等。所以文中,去掉AudioStream的描述。
2. 阅读AudioFocus内部实现机制后,对一些描述更加明确化。
Android中的Audio播放:竞争Audio之Audio Focus的应用的更多相关文章
- android中使用setVideoURI()播放视频
最近在做一个demo,要求播放视频,记录一下.使用的是VideoView控件,如果播放网络视频的话,视频应该是渐进流式的,格式嘛,大家应该都知道,一般是H.263或者H.264格式的扩展名为3gp或者 ...
- Android中的音频播放(MediaPlayer和SoundPool)
Android中音频和视频的播放我们最先想到的就是MediaPlayer类了,该类提供了播放.暂停.停止.和重复播放等方法.该类位于android.media包下,详见API文档.其实除了这个类还有一 ...
- Android中使用VideoView 播放视频
VideoView一般结合MediaController类使用,它会提供一个友好的图形界面,通过该界面可以控制视频的播放 package com.test.videoview; import andr ...
- Android中的Audio播放:控制Audio输出通道切换
Audio 输出通道有很多,Speaker.headset.bluetooth A2DP等.通话或播放音乐等使用Audio输出过程中,可能发生Audio输出通道的切换.比如,插入有线耳机播放音乐时,声 ...
- audio元素和video元素在ios和andriod中无法自动播放
原因: 因为各大浏览器都为了节省流量,做出了优化,在用户没有行为动作时(交互)不予许自动播放: /音频,写法一 <audio src="music/bg.mp3" autop ...
- [深入理解Android卷一全文-第七章]深入理解Audio系统
由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. ...
- Android中通过耳机按键控制音乐播放的实现
今天在研究Android中实现Android 4.2.2源码中的Music应用的源码,关于通过耳机按键控制音乐播放的实现,有点好奇,就仔细分析了一下源码, 主要由 MediaButtonIntentR ...
- Android中播放音乐的几种方式
前言 前几天一直在研究RxJava2,也写了记录了几篇博客,但因为工作任务原因,需要研究音频相关的知识,暂时放下Rxjava,本文的demo中,MediaPalyer 部分使用RxJava编写一点逻辑 ...
- JS audio播放一个的时候,其他正在播放的关闭
audio在使用中,如果有多个,在播放的时候,如果一个声音没有播放完继续下一个的话,原来正在播放的并不会关闭(在Android和PC上测试是这样,苹果产品不清楚) 现在需要做的是,当播放其中一个的时候 ...
随机推荐
- java实战
1.http://learning.happymmall.com/ http://www.happymmall.com/index.html 前台官网 http://test.happymmall. ...
- java 其它可选方法
异常处理的一个原则时,只有当你在知道如何处理的情况下才捕获异常,异常处理的一个重要目标时将错误处理代码同错误发生的地点相分离. "被检查异常"强制你在还没准备好处理错误的时候被迫加 ...
- InnoDB Lock浅谈
数据库使用锁是为了支持更好的并发,提供数据的完整性和一致性.InnoDB是一个支持行锁的存储引擎,锁的类型有:共享锁(S).排他锁(X).意向共享(IS).意向排他(IX).为了提供更好的并发,Inn ...
- py-faster-rcnn在windows下安装
准备好vs2013+anaconda2+好点的显卡(比如GTX970) 先改造caffe-rfcn以支持cudnn5,得到caffe-rfcn-cudnn5:替换个别代码文件和代码:改.props 再 ...
- centos redis 自动重启
配置init脚本 对于Centos,有一份https://gist.github.com/1335694 经过修改,如下: ########################## PATH=/usr/l ...
- Caffe训练AlexNet网络模型——问题三
caffe 进行自己的imageNet训练分类:loss一直是87.3365,accuracy一直是0 解决方法: http://blog.csdn.net/jkfdqjjy/article/deta ...
- 文件基本操作 (C语言)
一切皆文件 ---Linux 头文件 <stdio.h> 中定义了文件的相关操作 #include <stdio.h> 文件操作基本流程: 打开:fopen 相关操作 关闭:f ...
- PAT 1066. Root of AVL Tree (25)
An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child sub ...
- jquery 点击页面流畅弹出预定文字
js代码: <script src="https://cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script&g ...
- 心跳包(HeartBeat)
http://itindex.net/detail/52922-%E5%BF%83%E8%B7%B3-heartbeat-coderzh 几乎所有的网游服务端都有心跳包(HeartBeat或Ping) ...