Android音频焦点详解(上)
转载请注明出处: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