文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei。
private final static String AUDIO_PATH =http://www.androidbook.com/akc/filestorage/android/documentfiles/3389/play.mp3;
private MediaPlayer mediaPlayer = null;
private int playbackPosition = 0;
/* 【1】需要INTERNET的权限,否则报错误:setDataSource failed.:status=0x80000000, 并由此出现MediaPlayer的unable to create player异常,由于此错误没有提示是权限问题,故不容易修复。
* 【2】在reference中有张状态机的图,但是不需要那么复杂,也可以很好地理解,就是:初始化-》准备-》播放
* 【3】如果格式不支持,例如播放wma格式,并不能不抓的异常,但是会出现MediaPlayer的错误:error(1,-2147483648)。属于MediaPlayer在播放时出现的错误,出现在start()之后,可以用setOnErrorListener进行捕抓,但是也就是给了那两个int,没什么有效信息 */
private void startPlayWebAudio(String url) throws Exception{
killMediaPlayer();
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
Log.w("WEI","MediaPlayer is prepeared..");
mp.start(); //开始播放
}
});
//由于audio内容,来自网络,下载需要时间,如果在UI线程,使用mediaPlayer.prepare()进行准备播放,很容易造成ANR异常, 所以采用异步的方式,并通过onPreparedListenner来获得转变成功的回调函数
mediaPlayer.prepareAsync(); //准备过程,包括从网络下载
}
public void pausePlayingAudio( ){
if(mediaPlayer != null && mediaPlayer.isPlaying()){
playbackPosition = mediaPlayer.getCurrentPosition(); //记录当前位置,以便restart时在该位置开始播放
mediaPlayer.pause(); //暂停
Log.w("WEI","stop at " + playbackPosition/1000.0f + " secs.");
}
}
public void restartPlayingAudio( ){
if(mediaPlayer != null && !mediaPlayer.isPlaying()){
mediaPlayer.seekTo(playbackPosition); //从指定位置开始播放
mediaPlayer.start(); //播放
}
}
public void stopPlayingAudio( ){
if(mediaPlayer != null){
//停止。对于stop(),如果需要start(),要先进行prepare()。但对于pause(),可以直接进行start()。
mediaPlayer.stop();
playbackPosition = 0;
}
}
private void killMediaPlayer(){
if(mediaPlayer != null){
try{
//应确保APP在不是用mediaPlayer的时候进行release()。对于Activity,可以在onDestory()中进行释放。
mediaPlayer.release();
mediaPlayer = null;
}catch(Exception e){
e.printStackTrace();
}
}
}
从抓包的情况看,通过HTTP GET请求获得音频文件,音频文件从该TCP通道返回,累计到一定内容时,开始播放,一边播放,一边接受。
对于web内容,出了支持http外,MediaPlay支持rtsp协议、HTTP/HTTPS live streaming,以及M3U播放列表。
如果循环播放,可以使用:
mediaPlayer.setLooping(true);
有时,我们需要监控什么时候播放结束,如果只播放一次,可能需要stop()和release(),或者播放其他的音频。实现方式如下:
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(getApplicationContext(), "播放结束", Toast.LENGTH_SHORT).show();
mp.stop(); //可能还需要加上release(),看需求,是结束播放,还是下一首。
}
});
(1)使用resource ID创建MediaPlayer对象的方式
将音频文件放置在res/raw/下,则作为apk的部分进行打包,并看通过R.raw.xxx对资源进行调用。对于这类音频内容,播放的代码片段如下:
private void startPlayRawAudio(int resourceId){ //例如R.raw.test(放入test.mp3)
killMediaPlayer();
mediaPlayer = MediaPlayer.create(this, resourceId);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.start(); //在此不需要prepare过程。 因为MediaPlayer.create(context,resourceID)相当于执行了new MediaPlayer(context, resourceID),以及prepare()。不需要再次进行prepare。由于资源就在本地,可以马上返回,不需要进行异步准备。
}
(2)采用file descriptor方式
对于本地音频,media player可以通过文件描述符file descriptor来获取,代码片段如下:
private void startPlayRawAndioUsingFileDescriptor(int resourceId) throws Exception{
killMediaPlayer();
AssetFileDescriptor fd = getResources().openRawResourceFd(resourceId);//resourceId例如R.raw.test,获取在/res/raw/下的音频文件的文件描述符
if(fd != null){
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//从音频文件的某个指定位置,播放到另一个指定文字,如果整个文件播放,可以简单地使用mediaPlayer.setDataSource(fd.getFileDescriptor());
mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
fd.close(); //通过setDataSource(),mediaPlayer获取资源,可关闭fd
mediaPlayer.prepare(); //本地资源,不需要等待,可以不采用异步处理的方式,当然用prepareAsync()也没有任何问题
mediaPlayer.start();
}
}
(3)对SD卡音频文件
对于存放在SD卡文件,可以和web音频那样,使用URL的方式,也可以使file descriptor的方式,使用URL更为简单。例如要播放SD卡的Music/songs.mp3,我们只需更换web音频例子中的URL地址即可,代码片段如下:
private final static String AUDIO_PATH =Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) + "/song.mp3";
注意,这需要android.permission.READ_EXTERNAL_STORAGE的权限,否则会和要获取web资源但无访问Internet权限那样出错。
音乐播放有时需要持续性播放,我们需要为MediaPlayer申请wake lock,相关代码如下,当然,不要忘记获取相关的权限"android.permission.WAKE_LOCK"。
mediaPlayer.setWakeMode(this, PowerManager.PARTIAL_WAKE_LOCK);
除了CPU外,WiFi也可能会关闭,如果音频资源来自网络,可以通过WiFi Lock来保持WiFi有效,相关的操作和wake lock相似。
//(1)创建WifiLock
WifiManager manager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiLock lock = manager.createWifiLock("cn.wei.flowingflying");
……
//(2)持有WifiLock。对于网络资源的获取,通常通过manager.isWifiEnabled()获取是否当前Wifi有效,需要ACCESS_WIFI_STATE的权限。可以在程序中通过manager.setWifiEnabled(true|flase)来开启或关闭Wifi,需要CHANGE_WIFI_STATE的权限。WifiManager还可以获取连接信息,包括MAC地址等等。
lock.acquire();
……
//(3)释放WifiLock
if(lock.isHeld())
lock.release();
在媒体播放的过程中,可能会出现来电,闹钟等,需要对正在播放的音乐进行暂停。我们可以通过AudioManager对这些事件进行监测,代码片段如下:
private AudioManager audioMrg = null;
private OnAudioFocusChangeListener audioListener = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
… ….
audioMrg = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
audioListener = new OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
debug("onAudioFocusChange code = " + focusChange);//我们的debug显示方法,用于跟踪信息
if(mediaPlayer == null)
return;
switch(focusChange){
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: //临时事情audio的focuse,例如有来电,进行音乐播放暂停操作
debug("AUDIOFOCUS_LOSS_TRANSIENT");
pausePlayingAudio( );
break;
case AudioManager.AUDIOFOCUS_GAIN: //获得audio的focuse,对暂停的音乐继续播放
debug("AUDIOFOCUS_GAIN");
restartPlayingAudio( );
break;
case AudioManager.AUDIOFOCUS_LOSS:
debug("AUDIOFOCUS_LOSS");
stopPlayingAudio(null);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK://例如有短信的通知音,可以调低声音,无需silent
debug("AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); 。
//可以通过AudioManager.get调低声音,或简单地不做处理。
debug("当前音量:"+ audioMrg.getStreamVolume(AudioManager.AUDIOFOCUS_GAIN));
break;
default:
break;
}
}
};
int result = audioMrg.requestAudioFocus(audioListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);//返回granted或者failed,根据android的reference,对于市场未知的,例如播放音乐或者视频,采用AUDIOFOCUS_GAIN。对于短时间的,例如通知音,看采用AUDIOFOCUS_GAIN_TRANSIENT;允许叠加,共同放音,用AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;对于不希望系统声音干扰的,例如进行memo录音、语音识别,采用AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE。
if(result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED){
debug("requestAudioFocus : granted");
}else if(result == AudioManager.AUDIOFOCUS_REQUEST_FAILED){
debug("requestAudioFocus : failed");
}
}
@Override
protected void onDestroy() {
… ….
audioMrg.abandonAudioFocus(audioListener);
super.onDestroy();
}
一旦我们设置了Data source,就很难去修改,可以通过reset()对MediaPlayer进行重新初始化,或者另外创建一个新的MediaPlayer对象。在prepare后,可以通过getCurrentPosition()、getDuration()、isPlaying()来检查当前状态,可以使用setLooping()和setVolume()进行设置。
小例子代码在:Pro Android学习:media framworks小例子
相关链接:我的Android开发相关文章