Android MediaPlayer媒体源
发布日期:2021-06-30 21:23:12 浏览次数:2 分类:技术文章

本文共 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 WeakReference
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()中回收资源即可

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

 

转载地址:https://liwangjiang.blog.csdn.net/article/details/86736114 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:MMs 到 MediaModel . 彩信的各种mediaModel
下一篇:Java Object对象中的wait,notify,notifyAll通俗理解

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年05月02日 08时06分23秒