Android媒体播放:MediaPlayer

一、基本的类
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)的播放器。某些操作仅当播放器处在特定的状态才有效。如果操作时状态不对,系统会抛出异常或导致其他未期望的行为。具体请看下图:
Android媒体播放:MediaPlayer_第1张图片

五、释放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

你可能感兴趣的:(mediaplayer)