zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android开发之Mediaplayer

Android开发 MediaPlayer
2023-09-14 08:56:49 时间
上面提到过,Android下对于音频、视频的支持均需要使用到MediaPlayer,它主要用来控制Android下播放文件或流的类。MediaPlayer处于Android多媒体包下"android.media.MediaPlayer",仅有一个无参的构造函数,虽然仅为我们提供了一个无参的构造函数,为了方便我们初始化,还为我们提供了几个静态的create()方法用于完成MediaPlayer初始化的工作。

static MediaPlayer create(Context context,int resid):通过音频资源的Id来创建一个MediaPlayer实例。 static MediaPlayer create(Context context,Uri uri):通过一个音频资源的Uri地址来创建一个MediaPlayer实例。 MediaPlayer除了通过上面两个create()方法在初始化的时候指定媒体资源,还可以通过MediaPlayer.setDataSource()方法为初始化后的MediaPlayer设置媒体资源,setDataSource()具有多个重载函数,适用于不同的媒体资源来源,以下讲解几个常用的,其他的可以查阅官方文档。

void setDataSource(String path):通过一个媒体资源的地址指定MediaPlayer的数据源,这里的path可以是一个本地路径,也可以是网络路径。 void setDataSource(Context context,Uri uri):通过一个Uri指定MediaPlayer的数据源,这里的Uri可以是网络路径或这一个内容提供者的Uri。 void setDataSource(FileDescriptor fd):通过一个FileDescriptor指定一个MediaPlayer的数据源。  

MediaPlayer的音频源

通过上面介绍的初始化MediaPlayer的播放时媒体数据源的方法可以看出,MediaPlayer支持的数据源有:本地文件、内部的Uri(内容提供者)、外部Uri。

如,设置一个本地SD卡的资源:

1 mediaPlayer = new MediaPlayer();

2 mediaPlayer.setDataSource("/sdcarc/a.mp3");
注意读内存卡,还需要设定访问内存卡的权限:

 uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/ 
如,设置一个外部uri的网络流媒体资源:

1 mediaPlayer = new MediaPlayer();

2 mediaPlayer.setDataSource(http://192.168.1.102:1231/music/a.mp3);
如果访问网络流媒体资源,还需要设置访问网络的权限:

 uses-permission android:name="android.permission.INTERNET"/ 
 

 

使用MediaPlayer播放音乐

MediaPlayer其实是一个封装的很好的音频、视频流媒体操作类,如果查看其源码,会发现其内部是调用的native方法,所以它其实是有C++实现的。

既然是一个流媒体操作类,那么必然涉及到,播放、暂停、停止等操作,实际上MediaPlayer也为我们提供了相应的方法来直接操作流媒体。

void start():开始或恢复播放。 void stop():停止播放。 void pause():暂停播放。 通过上面三个方法,只要设定好流媒体数据源,即可在应用中播放流媒体资源,为了更好的操作流媒体,MediaPlayer还为我们提供了一些其他的方法,这里列出一些常用的,详细内容参阅官方文档。

int getDuration():获取流媒体的总播放时长,单位是毫秒。 int getCurrentPosition():获取当前流媒体的播放的位置,单位是毫秒。 void seekTo(int msec):设置当前MediaPlayer的播放位置,单位是毫秒。 void setLooping(boolean looping):设置是否循环播放。 boolean isLooping():判断是否循环播放。 boolean  isPlaying():判断是否正在播放。 void prepare():同步的方式装载流媒体文件。 void prepareAsync():异步的方式装载流媒体文件。 void release ():回收流媒体资源。  void setAudioStreamType(int streamtype):设置播放流媒体类型。 void setWakeMode(Context context, int mode):设置CPU唤醒的状态。 setNextMediaPlayer(MediaPlayer next):设置当前流媒体播放完毕,下一个播放的MediaPlayer。 大部分方法的看方法名就可以理解,但是有几个方法需要单独说明一下。

在使用MediaPlayer播放一段流媒体的时候,需要使用prepare()或prepareAsync()方法把流媒体装载进MediaPlayer,才可以调用start()方法播放流媒体。

setAudioStreamType()方法用于指定播放流媒体的类型,它传递的是一个int类型的数据,均以常量定义在AudioManager类中, 一般我们播放音频文件,设置为AudioManager.STREAM_MUSIC即可。

除了上面介绍的一些方法外,MediaPlayer还提供了一些事件的回调函数,这里介绍几个常用的:

setOnCompletionListener(MediaPlayer.OnCompletionListener listener):当流媒体播放完毕的时候回调。 setOnErrorListener(MediaPlayer.OnErrorListener listener):当播放中发生错误的时候回调。 setOnPreparedListener(MediaPlayer.OnPreparedListener listener):当装载流媒体完毕的时候回调。 setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener):当使用seekTo()设置播放位置的时候回调。  

MediaPlayer使用技巧

在使用MediaPlayer的使用过程中,有个小技巧需要说明一下:

1、在使用start()播放流媒体之前,需要装载流媒体资源。这里最好使用prepareAsync()用异步的方式装载流媒体资源。因为流媒体资源的装载是会消耗系统资源的,在一些硬件不理想的设备上,如果使用prepare()同步的方式装载资源,可能会造成UI界面的卡顿,这是非常影响用于体验的。因为推荐使用异步装载的方式,为了避免还没有装载完成就调用start()而报错的问题,需要绑定MediaPlayer.setOnPreparedListener()事件,它将在异步装载完成之后回调。异步装载还有一个好处就是避免装载超时引发ANR((Application Not Responding)错误。

复制代码
 1 mediaPlayer = new MediaPlayer();

 2 mediaPlayer.setDataSource(path);

 3 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

 5 // 通过异步的方式装载媒体资源

 6 mediaPlayer.prepareAsync();

 7 mediaPlayer.setOnPreparedListener(new OnPreparedListener() { 

 8 @Override

 9 public void onPrepared(MediaPlayer mp) {

10 // 装载完毕回调

11 mediaPlayer.start();

13 }); 
复制代码 2、使用完MediaPlayer需要回收资源。MediaPlayer是很消耗系统资源的,所以在使用完MediaPlayer,不要等待系统自动回收,最好是主动回收资源。

1 if (mediaPlayer != null mediaPlayer.isPlaying()) {

2 mediaPlayer.stop();

3 mediaPlayer.release();

4 mediaPlayer = null;

5 }
3、使用MediaPlayer最好使用一个Service来使用,并且在Service的onDestory()方法中回收MediaPlayer资源,实际上,就算是直接使用Activity承载MediaPlayer,也最好在销毁的时候判断一下MediaPlayer是否被回收,如果未被回收,回收其资源,因为底层调用的native方法,如果不销毁还是会在底层继续播放,而承载的组件已经被销毁了,这个时候就无法获取到这个MediaPlayer进而控制它。

复制代码
1 @Override

2 protected void onDestroy() {

3 if (mediaPlayer != null mediaPlayer.isPlaying()) {

4 mediaPlayer.stop();

5 mediaPlayer.release();

6 mediaPlayer = null;

8 super.onDestroy();

9 }
复制代码 4、对于单曲循环之类的操作,除了可以使用setLooping()方法进行设置之外,还可以为MediaPlayer注册回调函数,MediaPlayer.setOnCompletionListener(),它会在MediaPlayer播放完毕被回调。

复制代码
 1 // 设置循环播放

 2 // mediaPlayer.setLooping(true);

 3 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

 5 @Override

 6 public void onCompletion(MediaPlayer mp) {

 7 // 在播放完毕被回调

 8 play(); 

10 });
复制代码   5、因为MediaPlayer一直操作的是一个流媒体,所以无可避免的可能一段流媒体资源,前半段可以正常播放,而中间一段因为解析或者源文件错误等问题,造成中间一段无法播放问题,需要我们处理这个错误,否则会影响Ux(用户体验)。可以为MediaPlayer注册回调函数setOnErrorListener()来设置出错之后的解决办法,一般重新播放或者播放下一个流媒体即可。

复制代码
1 mediaPlayer.setOnErrorListener(new OnErrorListener() {

3 @Override

4 public boolean onError(MediaPlayer mp, int what, int extra) {

5 play();

6 return false;

8 });
复制代码  

 

Demo--一个简单的MP3播放器

上面已经介绍了MediaPlayer播放一段音频文件的所有需要用到的内容。下面通过一个简单的Demo来演示如何使用MediaPlayer播放一个SD卡上的MP3文件。操作MediaPlayer应该放在Service中完成,这里为了简单,使用Activity直接操作MediaPlayer。代码注释里写的很清楚里,这里不再累述。

执行这个示例需要在/sdcard/目录下存在xm.mp3的文件。

复制代码
 1 package cn.bgxt.mediaplayerdemo;

 3 import java.io.File;

 4 import android.media.AudioManager;

 5 import android.media.MediaPlayer;

 6 import android.media.MediaPlayer.OnCompletionListener;

 7 import android.media.MediaPlayer.OnErrorListener;

 8 import android.media.MediaPlayer.OnPreparedListener;

 9 import android.os.Bundle;

 10 import android.app.Activity;

 11 import android.view.View;

 12 import android.widget.Button;

 13 import android.widget.EditText;

 14 import android.widget.Toast;

 16 public class MainActivity extends Activity {

 17 private EditText et_path;

 18 private Button btn_play, btn_pause, btn_replay, btn_stop;

 19 private MediaPlayer mediaPlayer;

 21 @Override

 22 protected void onCreate(Bundle savedInstanceState) {

 23 super.onCreate(savedInstanceState);

 24 setContentView(R.layout.activity_main);

 26 et_path = (EditText) findViewById(R.id.et_path);

 27 btn_play = (Button) findViewById(R.id.btn_play);

 28 btn_pause = (Button) findViewById(R.id.btn_pause);

 29 btn_replay = (Button) findViewById(R.id.btn_replay);

 30 btn_stop = (Button) findViewById(R.id.btn_stop);

 32 btn_play.setOnClickListener(click);

 33 btn_pause.setOnClickListener(click);

 34 btn_replay.setOnClickListener(click);

 35 btn_stop.setOnClickListener(click);

 36 }

 38 private View.OnClickListener click = new View.OnClickListener() {

 40 @Override

 41 public void onClick(View v) {

 43 switch (v.getId()) {

 44 case R.id.btn_play:

 45 play();

 46 break;

 47 case R.id.btn_pause:

 48 pause();

 49 break;

 50 case R.id.btn_replay:

 51 replay();

 52 break;

 53 case R.id.btn_stop:

 54 stop();

 55 break;

 56 default:

 57 break;

 58 }

 59 }

 60 };

 61 /**

 62 * 播放音乐

 63 */

 64 protected void play() {

 65 String path = et_path.getText().toString().trim();

 66 File file = new File(path);

 67 if (file.exists() file.length() 0) {

 68 try {

 69 mediaPlayer = new MediaPlayer();

 70 // 设置指定的流媒体地址

 71 mediaPlayer.setDataSource(path);

 72 // 设置音频流的类型

 73 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

 75 // 通过异步的方式装载媒体资源

 76 mediaPlayer.prepareAsync();

 77 mediaPlayer.setOnPreparedListener(new OnPreparedListener() {

 78 @Override

 79 public void onPrepared(MediaPlayer mp) {

 80 // 装载完毕 开始播放流媒体

 81 mediaPlayer.start();

 82 Toast.makeText(MainActivity.this, "开始播放", 0).show();

 83 // 避免重复播放,把播放按钮设置为不可用

 84 btn_play.setEnabled(false);

 85 }

 86 });

 87 // 设置循环播放

 88 // mediaPlayer.setLooping(true);

 89 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

 91 @Override

 92 public void onCompletion(MediaPlayer mp) {

 93 // 在播放完毕被回调

 94 btn_play.setEnabled(true);

 95 }

 96 });

 98 mediaPlayer.setOnErrorListener(new OnErrorListener() {

100 @Override

101 public boolean onError(MediaPlayer mp, int what, int extra) {

102 // 如果发生错误,重新播放

103 replay();

104 return false;

105 }

106 });

107 } catch (Exception e) {

108 e.printStackTrace();

109 Toast.makeText(this, "播放失败", 0).show();

110 }

111 } else {

112 Toast.makeText(this, "文件不存在", 0).show();

113 }

115 }

116 /**

117 * 暂停

118 */

119 protected void pause() {

120 if (btn_pause.getText().toString().trim().equals("继续")) {

121 btn_pause.setText("暂停");

122 mediaPlayer.start();

123 Toast.makeText(this, "继续播放", 0).show();

124 return;

125 }

126 if (mediaPlayer != null mediaPlayer.isPlaying()) {

127 mediaPlayer.pause();

128 btn_pause.setText("继续");

129 Toast.makeText(this, "暂停播放", 0).show();

130 }

132 }

134 /**

135 * 重新播放

136 */

137 protected void replay() {

138 if (mediaPlayer != null mediaPlayer.isPlaying()) {

139 mediaPlayer.seekTo(0);

140 Toast.makeText(this, "重新播放", 0).show();

141 btn_pause.setText("暂停");

142 return;

143 } 

144 play();

145 }

147 /**

148 * 停止播放

149 */

150 protected void stop() {

151 if (mediaPlayer != null mediaPlayer.isPlaying()) {

152 mediaPlayer.stop();

153 mediaPlayer.release();

154 mediaPlayer = null;

155 btn_play.setEnabled(true);

156 Toast.makeText(this, "停止播放", 0).show();

157 }

159 }

161 @Override

162 protected void onDestroy() {

163 // 在activity结束的时候回收资源

164 if (mediaPlayer != null mediaPlayer.isPlaying()) {

165 mediaPlayer.stop();

166 mediaPlayer.release();

167 mediaPlayer = null;

168 }

169 super.onDestroy();

170 }

171 }
复制代码  

效果展示: