一、基本的类
MediaPlayer:负责播放音频和视频。
AudioManager:用来管理音频资源和音频输出。
二、使用MediaPlayer
播放一个从ContentResolver获得的系统Uri:
// 初始化uri
Uri myUri = ....;
MediaPlayer mediaPlayer = new MediaPlayer();
// 设置流媒体的类型,便于系统依类型控制音量(铃声音量、多媒体音量等)
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
// 设置数据源
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
播放网络流媒体:
// 注意:这个音频文件必须能够渐进式下载
String url = "http://........";
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
// 可能需要较长时间,比如缓冲
mediaPlayer.prepare();
mediaPlayer.start();
播放应用的内置raw资源:
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
// create()中已调用prepare(),不必再调用后者 mediaPlayer.start();
三、异步准备资源
prepare()
可能会花较长时间去提取、解码,为了避免阻塞UI主线程,Android提供了prepareAsync()
来实现异步准备。此方法会在后台开始准备并立即返回,当准备完毕时,系统会调用onPrepared()
:
private class OnPreparedListener implements MediaPlayer.OnPreparedListener {
@Override
public void onPrepared(MediaPlayer mp) {
// 异步准备完毕后,开始播放
mp.start();
}
}
// 如果是同步调用prepare(),发生错误时系统会直接抛出异常。
// 而异步调用prepareAsync()则需要监听错误的发生并处理。
private class OnErrorListener implements MediaPlayer.OnErrorListener {
/** * 错误发生时的回调方法。 * * 返回true表示错误已处理; * 如果返回false,或者未设置OnErrorListener, * 将会调用OnCompletionListener。 */
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// 处理错误代码略
...
// 此时MediaPlayer已处于Error状态,必须reset()
mp.reset();
return true;
}
}
Uri myUri = ....;
MediaPlayer mediaPlayer = new MediaPlayer();
// 设置异步准备监听器
mediaPlayer.setOnPreparedListener(new OnPreparedListener());
// 设置错误监听器
mediaPlayer.setOnErrorListener(new OnErrorListener());
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
// 异步准备
mediaPlayer.prepareAsync();
四、管理MediaPlayer状态
MediaPlay是基于状态(state)的播放器。某些操作仅当播放器处在特定的状态才有效。如果操作时状态不对,系统会抛出异常或导致其他未期望的行为。具体请看下图:
五、释放MediaPlayer
MediaPlayer占用着大量宝贵的系统资源。当用不着时(比如在未播放的情况下Activity进入onStop()
),可调用release()
来释放系统给它分配的资源:
mediaPlayer.release();
mediaPlayer = null;
六、处理音频焦点
可能会有若干个应用于某个时刻正在或者准备要播放音频,若都申请音频焦点则可有序竞争避免同时播放降低用户体验。
private class OnAudioFocusChangeListener implements AudioManager.OnAudioFocusChangeListener {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
// 获得音频焦点:
case AudioManager.AUDIOFOCUS_GAIN:
if (mMediaPlayer == null) initMediaPlayer();
else if (!mMediaPlayer.isPlaying() && mIsPrepared) {
mMediaPlayer.start();
}
mMediaPlayer.setVolume(1.0f, 1.0f);
break;
// 较长时间地失去音频焦点:
case AudioManager.AUDIOFOCUS_LOSS:
if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
break;
// 短暂地失去音频焦点:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
break;
// 短暂地失去音频焦点,但可以低音量播放:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
}
// 如果系统版本在Android 2.2(API 8)之上,
// 则可通过AudioManager来处理音频焦点,
// 以获得更好的用户体验。
if (Build.VERSION.SDK_INT >= 8) {
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
OnAudioFocusChangeListener onAudioFocusChangeListener =
new OnAudioFocusChangeListener();
// AUDIOFOCUS_GAIN:常驻型焦点。还有短暂、独占等。
int result = audioManager.requestAudioFocus(onAudioFocusChangeListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 没法获得音频焦点
} else {
// 已获得音频焦点,可以开始播放
...
// 播放结束后,可以放弃音频焦点:
audioManager.abandonAudioFocus(
onAudioFocusChangeListener);
}
}
七、处理AUDIO_BECOMING_NOISY
当耳机被拔出或断开连接时,系统发出这个Intent,并转换至内置扬声器播放。通过接收它并通知播放服务暂停或者降低音量,可以避免扬声器过于嘈杂而降低用户体验。
在Activity中动态注册一个广播接收器:
private NoisyIntentReceiver mNoisyIntentReceiver = new MusicIntentReceiver();
@Override
protected void onCreate(Bundle savedInstanceState) {
...
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(mNoisyIntentReceiver, intentFilter);
}
@Override
protected void onDestroy() {
...
unregisterReceiver(mNoisyIntentReceiver);
}
广播接收器的代码:
public class NoisyIntentReceiver extends BroadcastReceiver {
private PlaybackService.PlaybackBinder mPlaybackBinder = null;
public void setPlaybackBinder(
PlaybackService.PlaybackBinder playbackBinder) {
mPlaybackBinder = playbackBinder;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
if (mPlaybackBinder != null &&
mPlaybackBinder.isPlaying()) {
mPlaybackBinder.pause();
}
}
}
}
八、使用硬件音量键来控制音量
默认情况下,音量键可控制当前播放的音频流。如果前台Activity此时未播放任何音频,那么按下音量键控制的是铃声音量。在Activity中可做如下设置:
@Overwrite
protected void onCreate(Bundle savedInstanceState) {
...
setVolumeControlStream(AudioManager.STREAM_MUSIC);
...
}
使得当Activity可见时,它可以按用户的预期来调整Activity的音量。
九、使用耳机播放控制键来控制音频播放
每当按下耳机播放控制键(包括播放、暂停、停止、下一曲、上一曲)时,系统都会广播ACTION_MEDIA_BUTTON。
在Activity中动态注册一个广播接收器:
private RemoteControlReceiver mRemoteControlReceiver = new RemoteControlReceiver();
private void onGetAudioFocus() {
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
registerReceiver(mRemoteControlReceiver, intentFilter);
}
private void onLoseAudioFocus() {
unregisterReceiver(mRemoteControlReceiver);
}
广播接收器的代码:
public class RemoteControlReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
// ...
break;
case KeyEvent.KEYCODE_MEDIA_PLAY:
// ...
break;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
// ...
break;
case KeyEvent.KEYCODE_MEDIA_STOP:
// ...
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
// ...
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
// ...
break;
case KeyEvent.KEYCODE_MEDIA_REWIND:
// ...
break;
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
// ...
break;
}
}
}
}
参考资料:
Android > Developer > API Guides > Media Playback
http://developer.android.com/intl/zh-cn/guide/topics/media/mediaplayer.html
Android > Developer > Training > Controlling Your App’s Volume and Playback
http://developer.android.com/intl/zh-cn/training/managing-audio/volume-playback.html