Audio Stream
Android为不同的应用场合定义了不同的Audio Stream: Voice Call, Ring, Music,Alarm, Notification, DTMF。 这些AudioStream是相互独立的,所以也有各自的音量
使用
最重要的类是 MediaPlayer
,获取、解码、播放
播放 res/raw 中的文件 举个🌰
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you
异步准备
可能需要比较长的时间,media 需要获取资源和解码,最好不要放在 UI 线程。
此时可以使用prepareAsync()
方法,这个方法在后台运行,当准备工资就绪之后在MediaPlayer.OnPreparedListener
返回,可以在回调函数setOnPreparedListener()
中设置
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// TODO: 15/10/27
}
});
管理状态
player 有特有的状态,当操作 player 时只能当 player 处理特定的状态中,否则会抛出异常或者其他一些想不到的情况。
举个🌰,当 creat一个新MediaPlayer
时,他处于idle状态,在这个状态中,可以初始化,调用setDataSource()
,这样就进入了Initialized 状态,接着我们调用prepare()
或者 prepareAsync()
,当MediaPlayer
准备完全之后,会进入Prepared状态,当start()
播放之后,可以调用 paused stopped 等;
状态流程如图
释放MediaPlayer
MediaPlayer会消耗系统资源,当不在需要的时候需要用release()
释放,
mediaPlayer.release();
mediaPlayer = null;
使用 Service MediaPlayer
…………
audio focus
按照AudioFocus的机制,在使用Audio之前,需要申请AudioFocus,在获得AudioFocus之后才可以使用Audio;如果有别的程序竞争你正在使用的Audio,你的程序需要在收到通知之后做停止播放或者降低声音的处理。值得指出的是,这种机制是需要合作完成的,需要所有使用Audio资源的程序都按照这种机制来做,而如果有程序在它失去AudioFocus的时候仍然在使用Audio,AudioFocus拿它也没办法。而这一点对于开放系统的Android来说很致命的:用户可能安装没遵守这种机制的程序,或者版本太老还没引入这种机制的程序,这最终会导致很差的用户体验。
当应用需要播放声音或者 notification 时,需要请求audio focus,一旦获取焦点之后,便可以播放声音,如果有很多别的audio focus,要么停止播放此时的音乐,或者lower it to a quiet level,再之后获取audio focus继续播放
获取/放弃AudioFocus的方法都在android.media.AudioManager中,获取AudioFocus用requestAudioFocus()
;用完之后,放弃AudioFocus,用abandonAudioFocus()
。
举🌰
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// could not get audio focus.
}
requestAudioFocus()
的第一个参数 是AudioManager.OnAudioFocusChangeListener
其中的onAudioFocusChange()
方法
🌰
class MyService extends Service
implements AudioManager.OnAudioFocusChangeListener {
// ....
public void onAudioFocusChange(int focusChange) {
// Do something based on focus change...
}
}
其onAudioFocusChange()方法是Audio Focus被抢占与再次获得通知的地方。所以,每个要使用AudioFocus的程序都要小心实现这个函数,保证AudioFocus实现的一致性。申请成功之后监听AudioFocus使用情况的Listener,参数 foucsChange
AUDIOFOCUS_GAIN
:获取到了焦点AUDIOFOCUS_LOSS
:失去焦点很长一段时间,必须停止所有的 audio playback,此时可以释放资源了,比如释放MediaPlayer
AUDIOFOCUS_LOSS_TRANSIENT
:暂时失去焦点,但是会之后获取到焦点。此时必须停止播放,但是资源不必释放,因为之后会获取焦点AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
: 暂时失去焦点,但是允许继续小声的播放音乐,而不必关闭播放 举个🌰
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (mMediaPlayer == null) initMediaPlayer();
else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
mMediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
下面的时序图描述了AudioFocus被抢占与再次获取的典型场景
- 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]
AudioFocus中虽然把AudioStream作为参数,但是AudioFocus的内部裁决机制并未针对AudioStream做什么特别的处理。AudioFocus的处理针对所有的申请者来说的,除了它自身内部作为Alert的申请者有点特殊外,其它一律平等。