本文共 11944 字,大约阅读时间需要 39 分钟。
要想利用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、网络资源:假如有一个网络资源。
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~1void 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中添加权限
上面代码只会确保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参数值如下
- AUDIOFOCUS_GAIN:获取audio focus
- AUDIOFOCUS_LOSS:失去audio focus很长一段时间,必须停止所有的audio播放,清理资源
- AUDIOFOCUS_ LOSS_TRANSIENT:暂时失去audio focus,但是很快就会重新获得,在此状态应该暂停所有音频播放,但是不能清除资源
- 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方法中注销广播即可。
@Overrideprotected void onDestroy() { super.onDestroy(); unregisterReceiver(broadcastReceiver);}
当然也可以使用静态广播
1、在manifest文件中注册一个广播
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的使用。首先看一下布局效果图:
布局效果图
在该实例中以加载网络资源为例,这里定义一个字符串数据保存网络URL。
private String[] musics = {"http://ibooker.cc/ibooker/musics/1234.mp3", "http://ibooker.cc/ibooker/musics/2345.mp3"}; // 设置音频资源(网络)
在程序一来是,需要对MediaPlayer进行初始化工作,所以定义一个全局变量MediaPlayer。
// 初始化MediaPlayerprivate 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();方法是用来显示播放进度。这里是通过开启一个线程来时时监听播放进度。
// 开启线程,修改descTvprivate 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 WeakReferencemActivity; 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()中回收资源即可
@Overrideprotected void onStop() { super.onStop(); // 释放mediaPlayer if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; }}
转载地址:https://liwangjiang.blog.csdn.net/article/details/86736114 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!