转载请注明出处:http://www.cnblogs.com/landptf/p/6384112.html


2017年开年第一篇博客,很早就想总结一下Android音频的相关知识。
今天我们先来看一下音频焦点的相关内容,分为上下篇,上篇主要介绍音频焦点在媒体类应用中的使用,下篇进行源码分析,我们学习源码不仅仅是为了更深入的了解,更主要的目的是学习Android的设计架构,为我们在平时开发过程中提供一些优秀的设计思想。
一 焦点在Android应用中的作用
我们的手机里经常会安装一些媒体类的应用,例如网易云音乐,QQ音乐,爱奇艺视频,优酷视频等等,你有没有想过,当我们听QQ音乐的歌曲时,切换到网易云音乐播放歌曲,或者打开爱奇艺观看视频时,QQ音乐播放的歌曲就会暂停,这是为什么呢?又是如何实现的呢?如果不暂停会是什么效果呢?
以上这些疑问都可以用音频焦点来解释,在下面的内容中你将找到答案。
二 焦点讲解
众所周知Android是一个开放的系统,为了协调各媒体应用之间的关系,Android增加了音频焦点管理机制,各应用开发者应尽量遵循该机制进行开发。
我们通过一个例子来讲解Android的焦点机制。
新建一个Activity,在其中增加一个播放暂停按钮,来控制音乐播放。
看一下部分关键代码,全部代码在文末会给出

private void initData() {
//1 初始化AudioManager对象
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//2 申请焦点
mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
AssetFileDescriptor fileDescriptor;
try {
//3 获取音频文件,我从网上下载的歌曲,放到了assets目录下
fileDescriptor = this.getAssets().openFd("littlelucky.mp3");
//4 实例化MediaPlayer对象
mMediaPlayer = new MediaPlayer();
//5 设置播放流类型
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//6 设置播放源,有多个参数可以选择,具体参考相关文档,本文旨在介绍音频焦点
mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),
fileDescriptor.getStartOffset(),
fileDescriptor.getLength());
//7 设置循环播放
mMediaPlayer.setLooping(true);
//8 准备监听
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
//9 准备完成后自动播放
mMediaPlayer.start();
}
});
//10 异步准备
mMediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}

涉及到的MediaPlayer播放音乐流程请自行查询相关文档,不在本文介绍范围
我们重点看一下第2步 申请焦点
通过AudioManager对象调用requestAudioFocus方法,有三个参数
OnAudioFocusChangeListener l,int streamType,int durationHint
1 焦点变化的监听器

private AudioManager.OnAudioFocusChangeListener mAudioFocusChange = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange){
case AudioManager.AUDIOFOCUS_LOSS:
//长时间丢失焦点,当其他应用申请的焦点为AUDIOFOCUS_GAIN时,
//会触发此回调事件,例如播放QQ音乐,网易云音乐等
//通常需要暂停音乐播放,若没有暂停播放就会出现和其他音乐同时输出声音
Log.d(TAG, "AUDIOFOCUS_LOSS");
stop();
//释放焦点,该方法可根据需要来决定是否调用
//若焦点释放掉之后,将不会再自动获得
mAudioManager.abandonAudioFocus(mAudioFocusChange);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
//短暂性丢失焦点,当其他应用申请AUDIOFOCUS_GAIN_TRANSIENT或AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE时,
//会触发此回调事件,例如播放短视频,拨打电话等。
//通常需要暂停音乐播放
stop();
Log.d(TAG, "AUDIOFOCUS_LOSS_TRANSIENT");
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
//短暂性丢失焦点并作降音处理
Log.d(TAG, "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
break;
case AudioManager.AUDIOFOCUS_GAIN:
//当其他应用申请焦点之后又释放焦点会触发此回调
//可重新播放音乐
Log.d(TAG, "AUDIOFOCUS_GAIN");
start();
break;
}
}
};

2 streamType音源类型,在AudioManager中定义

/** 通话 */
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;
/** 通知音 */
public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
/** @hide 蓝牙电话 */
public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
/** @hide 强制的系统声音 */
public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
/** DTMF拨号音 */
public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
/** @hide 文本识别音 */
public static final int STREAM_TTS = AudioSystem.STREAM_TTS;

3 durationHint 获得焦点的时间长短
在AudioManager中定义了四种类型

AUDIOFOCUS_GAIN //长时间获得焦点
AUDIOFOCUS_GAIN_TRANSIENT //短暂性获得焦点,用完应立即释放
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK //短暂性获得焦点并降音,可混音播放
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE //短暂性获得焦点,录音或者语音识别

我们可根据自己的应用的使用场景来合理的配置每个参数,并在焦点变化监听器来做相应的处理。
调试代码已上传github:https://github.com/landptf/BlogDemo

Android音频焦点详解(上)的更多相关文章

  1. android屏幕适配详解

    android屏幕适配详解 官方地址:http://developer.android.com/guide/practices/screens_support.html 一.关于布局适配建议 1.不要 ...

  2. Android系统目录结构详解

    Android系统基于linux内核.JAVA应用,算是一个小巧精致的系统.虽是开源,但不像Linux一般庞大,娇小可亲,于是国内厂商纷纷开发出自己基于Android的操作系统.在此呼吁各大厂商眼光放 ...

  3. android:ToolBar详解

    android:ToolBar详解(手把手教程) 泡在网上的日子 发表于 2014-11-18 12:49 第 124857 次阅读 ToolBar 42 来源 http://blog.mosil.b ...

  4. Android之canvas详解

    首先说一下canvas类: Class Overview The Canvas class holds the "draw" calls. To draw something, y ...

  5. 【转】Android Canvas绘图详解(图文)

    转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡 ...

  6. Android 核心分析 之八Android 启动过程详解

    Android 启动过程详解 Android从Linux系统启动有4个步骤: (1) init进程启动 (2) Native服务启动 (3) System Server,Android服务启动 (4) ...

  7. Android GLSurfaceView用法详解(二)

    输入如何处理       若是开发一个交互型的应用(如游戏),通常需要子类化 GLSurfaceView,由此可以获取输入事件.下面有个例子: java代码: package eoe.ClearTes ...

  8. Android编译过程详解(一)

    Android编译过程详解(一) 注:本文转载自Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359 ...

  9. IE8“开发人员工具”使用详解上(各级菜单详解)

    来源: http://www.cnblogs.com/JustinYoung/archive/2009/03/24/kaifarenyuangongju.html IE8“开发人员工具”使用详解上(各 ...

随机推荐

  1. LNMP的安装

    一.安装Linux 安装某个linux桌面版系统,基本是ubuntu即可. 安装必要的库,如:pcre.xml.openssl.zlib等,sudo apt-get install libpcre3 ...

  2. eclipse创建Maven父子结构Maven项目

    1.创建聚合模块 选择菜单项 File—>New—>Other,在弹出的对话框中选择Maven下的Maven Project,然后单击Next按钮,在弹出的New Maven Projec ...

  3. mysql root密码

    方法1: 用SET PASSWORD命令 首先登录MySQL. 格式:mysql> set password for 用户名@localhost = password('新密码'); 例子:my ...

  4. TcpView 查看端口的小工具(推荐)

    介绍: TCPView是一个Windows程序,将显示你的详细清单的所有TCP和UDP端点在您的系统,包括拥有进程名称,远程地址和状态的TCP连接. 打开下面的链接就可以下载了. https://te ...

  5. S3C2440硬件连接解析

    S3c2440是三星公司推出的一款基于ARM920T的处理器,采用ARM内核,不同于单片机,无片上rom与ram,必须搭配相应的外围电路进行使用,现在,让我们从零开始进行这一块MCU的学习,为了入门简 ...

  6. 关于Java在Linux or Android平台调用.so库

    Linux平台Java调用so库-JNI使用例子 android NDK开发及调用标准linux动态库.so文件 在Android项目中调用已有.so库 Android 调用.so文件 jni And ...

  7. DateFormat 竟然是非线程安全的?!!!!!

    今天撸代码忽然发现一个奇怪的一场抛出,经过一番排查发现有可能DateFormat 的多线程问题造成的,网上一查DateFormat竟然非线程安全.那我原先的代码...(细思极恐)

  8. Struts2接受参数的几种类型和接受复杂类型参数(list<String>和list<Object>)

    Struts2接受参数的几种类型 大概有这几种类型: 1.使用Action的属性接受参数 在Action中加入成员变量,配置Getter和Setter方法,Getter而和Setter方法的名字和表单 ...

  9. iOS 导航栏不可点击

    self.navigationController.navigationBar.userInteractionEnabled = NO;

  10. iOS 之 后台下载,前台显示模式,双 block

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //耗时的操作 NSURL *url ...