Android MediaPlayer媒体源

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35427437/article/details/86736114

要想利用MediaPlayer实现音频的播放,首先要对MediaPlayer进行初始化工作,得到MediaPlayer对象,在通过MediaPlayer进行相应的操作。

一般过程:初始化MediaPlayer - 加载媒体源 - 准备 - 开始播放

MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource("...");
mediaPlayer.prepare();
mediaPlayer.start();

MediaPlayer支持多种不同的媒体源: 本地资源、内部的URI,比如一个你可能会从ContentResolver获取的uri、外部URL(流)。

1、raw文件中媒体源:假如res/raw文件中包含一个sound_music.mp3文件。

MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.sound_music);

2、assets文件中媒体源:假如在assets中包含一个sound_music.mp3文件。

try {
    AssetFileDescriptor fd = getAssets().openFd("sound_music.mp3");
    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
} catch (IOException e) {
    e.printStackTrace();
}

3、SD卡中媒体源:假如在SD卡中包含一个sound_music.mp3文件。

try {
    MediaPlayer mediaPlayer = new MediaPlayer();
    String path = "/sdcard/sound_music.mp3";
    mediaPlayer.setDataSource(path);
} catch (IOException e) {
    e.printStackTrace();
}

4、网络资源:假如有一个网络资源http://ibooker.cc/ibooker/musics/sound_music.mp3

MediaPlayer mediaPlayer = new MediaPlayer();
// 方式一
//    Uri uri = Uri.parse("http://ibooker.cc/ibooker/musics/sound_music.mp3");
//    mediaPlayer.setDataSource(this, uri);

// 方式二
mediaPlayer.setDataSource("http://ibooker.cc/ibooker/musics/sound_music.mp3");

MediaPlayer常用方法

int getCurrentPosition();// 得到当前播放位置(ms)
int getDuration();// 得到文件的时间(ms)
void setLooping(boolean var1);// 设置是否循环播放
boolean isLooping();// 是否循环播放
boolean isPlaying();// 是否正在播放
void pause();// 暂停
void prepare();// 同步准备
void prepareAsync();// 异步准备
void release();// 释放MediaPlayer对象
void reset();// 重置MediaPlayer对象
void seekTo(int msec);// 指定播放位置(以毫秒为单位)
void setDataSource(String path);// 设置播放资源
void setScreenOnWhilePlaying(boolean screenOn);// 设置播放的时候一直让屏幕变亮
void setWakeMode(Context context, int mode);// 设置唤醒模式
void setVolume(float leftVolume, float rightVolume);// 设置音量,参数分别表示左右声道声音大小,取值范围为0~1
void start();// 开始播放
void stop();// 停止播放

MediaPlayer常用事件监听

播放出错监听

MediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
    @Override
    public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
        return false;
    }
});

播放完成监听

MediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        // todo
    }
});

网络流媒体缓冲监听

MediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
    @Override
    public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
        // i 0~100
        Log.d("Progress:", "缓存进度" + i + "%");
    }
});

准备Prepared完成监听

MediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        // todo
    }
});

进度调整完成SeekComplete监听,主要是配合seekTo(int)方法

MediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
    @Override
    public void onSeekComplete(MediaPlayer mediaPlayer) {
        // todo
    }
});

使用锁

当音频在后台播放时,设备可能进入到休眠状态,此时系统会关闭一些不必要的资源,包括CPU和wifi等等...如果想要在后台播放或者缓冲音乐并且不想被系统干扰必须使用锁,并且在paused或者stopped状态下释放它。

// 设置设备进入锁状态模式-可在后台播放或者缓冲音乐-CPU一直工作
mediaPlayer.setWakeMode(this, PowerManager.PARTIAL_WAKE_LOCK);

同时需要在清单文件AndroidManifest.xml中添加权限

<uses-permission android:name="android.permission.WAKE_LOCK" />

上面代码只会确保cpu一直工作,如果你使用wifi播放流媒体,你还需要持有wifi锁

// 如果你使用wifi播放流媒体,你还需要持有wifi锁
WifiManager.WifiLock wifiLock = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE)).createWifiLock(WifiManager.WIFI_MODE_FULL, "wifilock");
wifiLock.acquire();

当你停止播放或者不再需要连接网络释放

wifiLock.release();

处理音频焦点

android是一个支持多任务的系统,这给使用audio的程序带来了一定的风险,因为可能几个程序会来竞争音频输出设备,在android2.2之前没有内置的机制来处理这个问题,导致体验很差。比如用户在听音乐时,另一个应用需要向用户提示一个非常重要的通知,但音乐声盖过了通知音,导致用户错失了最佳接收提示的时间,为了协调设备的音频输出,android提出了Audio Focus机机制,获取audio focus必须调用AudioManager的requestAudioFocus()方法。

// 处理音频焦点-处理多个程序会来竞争音频输出设备
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    // 征对于Android 8.0+
    AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
                    .setOnAudioFocusChangeListener(focusChangeListener).build();
    audioFocusRequest.acceptsDelayedFocusGain();
    audioManager.requestAudioFocus(audioFocusRequest);
} else {
    // 小于Android 8.0
    int result = audioManager.requestAudioFocus(focusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        // could not get audio focus.
    }
}

focusChange参数值如下

    1. AUDIOFOCUS_GAIN:获取audio focus
    1. AUDIOFOCUS_LOSS:失去audio focus很长一段时间,必须停止所有的audio播放,清理资源
    1. AUDIOFOCUS_ LOSS_TRANSIENT:暂时失去audio focus,但是很快就会重新获得,在此状态应该暂停所有音频播放,但是不能清除资源
    1. AUDIOFOCUS_ LOSS_TRANSIENT _CAN_DUCK:暂时失去 audio focus,但是允许持续播放音频(以很小的声音),不需要完全停止播放。
AudioManager.OnAudioFocusChangeListener focusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
    @Override
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // 获取audio focus
                if (mediaPlayer == null)
                    mediaPlayer = new MediaPlayer();
                else if (!mediaPlayer.isPlaying())
                    mediaPlayer.start();
                mediaPlayer.setVolume(1.0f, 1.0f);
                break;
            case AudioManager.AUDIOFOCUS_LOSS:
                // 失去audio focus很长一段时间,必须停止所有的audio播放,清理资源
                if (mediaPlayer.isPlaying())
                    mediaPlayer.stop();
                mediaPlayer.release();
                mediaPlayer = null;
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // 暂时失去audio focus,但是很快就会重新获得,在此状态应该暂停所有音频播放,但是不能清除资源
                if (mediaPlayer.isPlaying())
                    mediaPlayer.pause();
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                // 暂时失去 audio focus,但是允许持续播放音频(以很小的声音),不需要完全停止播放。
                if (mediaPlayer.isPlaying())
                    mediaPlayer.setVolume(0.1f, 0.1f);
                break;
        }
    }
};

处理AUDIO_ BECOMING_NOISY意图

当用户插着耳机听音乐突然拔掉耳机,如果没有对以上意图进行处理,音频将会通过外部扬声器来播放音频,这也许不会是用户想要的,所以我们必须对此处理。

可以使用动态广播broadcastReceiver

broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
            // 拔掉耳机时候进行相应的操作
        }
    }
};
registerReceiver(broadcastReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));

然后在页面onDestory方法中注销广播即可。

@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(broadcastReceiver);
}

当然也可以使用静态广播

1、在manifest文件中注册一个广播

<receiver android:name=".MusicIntentReceiver">
       <intent-filter> 
              <action android:name="android.media.AUDIO_BECOMING_NOISY" />              
      </intent-filter>
</receiver>

2、注册MusicIntentReceiver 广播来处理这个intent

public class MusicIntentReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context ctx, Intent intent) {
        if (intent.getAction().equals(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
            // signal your service to stop playback
            // (via an Intent, for instance)
        }
    }
}

检索媒体文件

// 通过ContentResolver来获取外部媒体文件
ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
    // query failed, handle error.
} else if (!cursor.moveToFirst()) {
    // no media on the device
} else {
    int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
    int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
    do {
        long thisId = cursor.getLong(idColumn);
        String thisTitle = cursor.getString(titleColumn);
        // ...process entry...
    } while (cursor.moveToNext());
}

// 配合MediaPlayer使用
try {
    long id = /* retrieve it from somewhere */;
    Uri contentUri = ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, thisId);
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.setDataSource(getApplicationContext(), contentUri);
    // ...prepare and start...
} catch (IOException e) {
    e.printStackTrace();
}

简易播放音乐器案例

常见的音乐播放器都有,上一首、下一首、播放、暂停、停止等功能,那接下来就用一个简易的音乐播放器来说明MediaPlayer的使用。首先看一下布局效果图:

布局效果图

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF">

    <LinearLayout
        android:id="@+id/layout_bottom"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:background="#F5F5F5"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_pre"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="上一首"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tv_pause"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="暂停"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tv_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="播放"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tv_stop"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="停止"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tv_next"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="下一首"
            android:textSize="14sp" />

    </LinearLayout>

    <TextView
        android:id="@+id/tv_music"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/layout_bottom"
        android:gravity="center"
        android:orientation="vertical"
        android:text="@string/app_name"
        android:textSize="20sp" />

</RelativeLayout>

在该实例中以加载网络资源为例,这里定义一个字符串数据保存网络URL。

private String[] musics = {"http://ibooker.cc/ibooker/musics/1234.mp3",
            "http://ibooker.cc/ibooker/musics/2345.mp3"}; // 设置音频资源(网络)

在程序一来是,需要对MediaPlayer进行初始化工作,所以定义一个全局变量MediaPlayer。

// 初始化MediaPlayer
private void initMediaPlayer() {
    if (mediaPlayer == null)
        mediaPlayer = new MediaPlayer();
    // 设置音量,参数分别表示左右声道声音大小,取值范围为0~1
    mediaPlayer.setVolume(0.5f, 0.5f);
    // 设置是否循环播放
    mediaPlayer.setLooping(false);
}

当点击播放按钮,程序要马上播放相应的音乐文件,因为是加载网络资源,所以在这里采用prepareAsync进行准备,同时设置OnPreparedListener监听是否准备完成。

// 播放
private void play() {
    try {
        if (mediaPlayer == null)
            initMediaPlayer();
        if (isPause) {
            mediaPlayer.start();
            updateDescTv();
        } else {
            // 重置mediaPlayer
            mediaPlayer.reset();
            // 重新加载音频资源
//         Uri uri = Uri.parse(musics[current_item]);
//         mediaPlayer.setDataSource(this, uri);
            mediaPlayer.setDataSource(musics[current_item]);
            // 准备播放(同步)-预期准备,因为setDataSource()方法之后,MediaPlayer并未真正的去装载那些音频文件,需要调用prepare()这个方法去准备音频
//         mediaPlayer.prepare();
            // 准备播放(异步)
            mediaPlayer.prepareAsync();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

注:isPause是用来标记当前播放是否处于暂停状态。

// 异步准备Prepared完成监听
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        // 开始播放
        mediaPlayer.start();
        updateDescTv();
    }
});

updateDescTv();方法是用来显示播放进度。这里是通过开启一个线程来时时监听播放进度。

// 开启线程,修改descTv
private void updateDescTv() {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                while (mediaPlayer != null && mediaPlayer.isPlaying()) {// 判断音频是否正在播放
                    myHandler.sendEmptyMessage(100);
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    if (executorService == null || executorService.isShutdown())
        executorService = Executors.newSingleThreadExecutor();
    executorService.execute(thread);
}

MyHandler myHandler = new MyHandler(this);

private static class MyHandler extends Handler {
    private WeakReference<Activity> mActivity;

    MyHandler(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity currentActivity = (MainActivity) mActivity.get();

        switch (msg.what) {
            case 100:
                if (currentActivity.mediaPlayer != null)
                    currentActivity.descTv.setText("播放进度:" + currentActivity.mediaPlayer.getCurrentPosition() * 100 / currentActivity.mediaPlayer.getDuration() + "%");
                break;
        }
    }
}

要想实现下一首和上一首的功能,还需要定义一个变量current_item标记当前播放哪一首。

// 下一首
private void nextMusic() {
    current_item++;
    if (current_item >= musics.length)
        current_item = 0;
    play();
}

// 上一首
private void preMusic() {
    current_item--;
    if (current_item < 0)
        current_item = musics.length - 1;
    play();
}

当点击暂停时

if (mediaPlayer.isPlaying()) {
    isPause = true;
    mediaPlayer.pause();
}

当点击停止时

if (mediaPlayer.isPlaying())
    mediaPlayer.reset();

最后在页面onStop()中回收资源即可

@Override
protected void onStop() {
    super.onStop();
    // 释放mediaPlayer
    if (mediaPlayer != null) {
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35427437/article/details/86736114