代码地址:https://github.com/huweigoodboy/SweetMusicPlayer
上一篇写了加载歌曲列表,http://blog.csdn.net/huweigoodboy/article/details/39856411,现在来总结下播放本地音乐。
首先来看看MediaPlayer的生命周期:
Idle 状态:当使用new()方法创建一个MediaPlayer对象或者调用了其reset()方法时,该MediaPlayer对象处于idle状态。这两种方法的一个重要差别就是:如果在这个状态下调用了getDuration()等方法(相当于调用时机不正确),通过reset()方法进入idle状态的话会触发OnErrorListener.onError(),并且MediaPlayer会进入Error状态;如果是新创建的MediaPlayer对象,则并不会触发onError(),也不会进入Error状态。
End 状态:通过release()方法可以进入End状态,只要MediaPlayer对象不再被使用,就应当尽快将其通过release()方法释放掉,以释放相关的软硬件组件资源,这其中有些资源是只有一份的(相当于临界资源)。如果MediaPlayer对象进入了End状态,则不会在进入任何其他状态了。
Initialized 状态:这个状态比较简单,MediaPlayer调用setDataSource()方法就进入Initialized状态,表示此时要播放的文件已经设置好了。
Prepared 状态:初始化完成之后还需要通过调用prepare()或prepareAsync()方法,这两个方法一个是同步的一个是异步的,只有进入Prepared状态,才表明MediaPlayer到目前为止都没有错误,可以进行文件播放。
Preparing 状态:这个状态比较好理解,主要是和prepareAsync()配合,如果异步准备完成,会触发OnPreparedListener.onPrepared(),进而进入Prepared状态。
Started 状态:显然,MediaPlayer一旦准备好,就可以调用start()方法,这样MediaPlayer就处于Started状态,这表明MediaPlayer正在播放文件过程中。可以使用isPlaying()测试MediaPlayer是否处于了Started状态。如果播放完毕,而又设置了循环播放,则MediaPlayer仍然会处于Started状态,类似的,如果在该状态下MediaPlayer调用了seekTo()或者start()方法均可以让MediaPlayer停留在Started状态。
Paused 状态:Started状态下MediaPlayer调用pause()方法可以暂停MediaPlayer,从而进入Paused状态,MediaPlayer暂停后再次调用start()则可以继续MediaPlayer的播放,转到Started状态,暂停状态时可以调用seekTo()方法,这是不会改变状态的。
Stop 状态:Started或者Paused状态下均可调用stop()停止MediaPlayer,而处于Stop状态的MediaPlayer要想重新播放,需要通过prepareAsync()和prepare()回到先前的Prepared状态重新开始才可以。
PlaybackCompleted状态:文件正常播放完毕,而又没有设置循环播放的话就进入该状态,并会触发OnCompletionListener的onCompletion()方法。此时可以调用start()方法重新从头播放文件,也可以stop()停止MediaPlayer,或者也可以seekTo()来重新定位播放位置。
Error状态:如果由于某种原因MediaPlayer出现了错误,会触发OnErrorListener.onError()事件,此时MediaPlayer即进入Error状态,及时捕捉并妥善处理这些错误是很重要的,可以帮助我们及时释放相关的软硬件资源,也可以改善用户体验。通过setOnErrorListener(android.media.MediaPlayer.OnErrorListener)可以设置该监听器。如果MediaPlayer进入了Error状态,可以通过调用reset()来恢复,使得MediaPlayer重新返回到Idle状态。
说明:
1,获取MediaPlayer的实例:
1)通过new方式:MediaPlayer mp=new MediaPlayer();
2) 通过create方式:MediaPlayer mp=MediaPlaer.create(this,R.raw.music);//此时就不需要再次setDataSource()了
2,设置播放资源
1)播放sd卡上资源
mp.setDataSource("/sdcard/music.mp3");
2)播放raw目录下的资源
MediaPlayer mp=MediaPlaer.create(this,R.raw.music);
3)网络媒体上的资源
mp.setDataSource("http://music.com/music.mp3");
3,控制方法
prepare()和prepareAsync() 提供了同步和异步两种方式设置播放器进入prepare状态,需要注意的是,如果MediaPlayer实例是由create方法创建的,那么第一次启动播放前不需要再调用prepare()了,因为create方法里已经调用过了。
start()是真正启动文件播放的方法,
pause()和stop()比较简单,起到暂停和停止播放的作用,
seekTo()是定位方法,可以让播放器从指定的位置开始播放,需要注意的是该方法是个异步方法,也就是说该方法返回时并不意味着定位完成,尤其是播放的网络文件,真正定位完成时会触发OnSeekComplete.onSeekComplete(),如果需要是可以调用setOnSeekCompleteListener(OnSeekCompleteListener)设置监听器来处理的。
release()可以释放播放器占用的资源,一旦确定不再使用播放器时应当尽早调用它释放资源。
reset()可以使播放器从Error状态中恢复过来,重新会到Idle状态。
4)设置播放器的监听器:
MediaPlayer提供了一些设置不同监听器的方法来更好地对播放器的工作状态进行监听,以期及时处理各种情况,
如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)等,设置播放器时需要考虑到播放器可能出现的情况设置好监听和处理逻辑,以保持播放器的健壮性。
上面部分多为引用,能够更好地理解MediaPlayer。
这里设置各种状态,如PLAY,PAUSE,STOP,PROGRESS_CHANGE,不同的状态对应不同的操作。
在服务启动后,应该去获取当前播放的歌曲,然后去new MediaPlayer()和设置setDateSource();
1,PLAY操作:
1)mp.prepare();
2)当mp准备好后,向ui发送广播,通知歌词界面,bottomactionbar更新信息。
3)mp.start();
2.PAUSE
1)移除广播
2)mp.pasue()
3,STOP
1)移除广播
2)mp.stop()
3)mp.reset()
4,PROGRESS_CHANGE
发送进度变更的广播
具体代码:
package com.huwei.sweetmusicplayer.services;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import com.huwei.sweetmusicplayer.R;
import com.huwei.sweetmusicplayer.datamanager.MusicManager;
import com.huwei.sweetmusicplayer.enums.MusicState;
import com.huwei.sweetmusicplayer.models.Song;
import com.huwei.sweetmusicplayer.ui.widgets.NotificationView;
import com.slidelib.MainActivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.RemoteViews;
public class LocalMusicService extends Service implements OnCompletionListener{
private static final String TAG="LocalMusicService";
private MediaPlayer mp; // mediaPlayer对象
private int currentTime;
private int duration;
private int id;
private Uri uri;
private Handler handler;
NotificationManager notificationManager;
private final int notiID=987654321;
private static final String MUSIC_CURRENT = "com.music.currentTime";
private static final String MUSIC_DURATION = "com.music.duration";
private static final String MUSIC_NEXT = "com.music.next";
private static final String MUSIC_UPDATE = "com.music.update";
@Override
public void onCreate() {
// TODO Auto-generated method stub
if (mp != null) {
mp.reset();
mp.release();
}
mp = new MediaPlayer();
//设置结束后的监听
mp.setOnCompletionListener(this);
}
void showMusicPlayerNotification(String tickerText, int id,
int resId, int photoId, String title, String artist){
notificationManager=(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification=new Notification(resId, title, System.currentTimeMillis());
notification.flags|=Notification.FLAG_NO_CLEAR ;
notification.flags|=Notification.FLAG_INSISTENT ;
RemoteViews reViews=new RemoteViews(getPackageName(),R.layout.notification_play);
reViews.setTextViewText(R.id.title, title);
reViews.setTextViewText(R.id.text, artist);
reViews.setImageViewResource(R.id.imageview_notification_play, photoId);
Intent intent=new Intent(this,MainActivity.class);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent=PendingIntent.getActivity(getBaseContext(), 0, intent, 0);
Log.i(TAG, "pendingIntent:"+pendingIntent);
notification.contentView=reViews;
//notification.bigContentView=reViews;
notification.contentIntent=pendingIntent;
notificationManager.notify(id, notification);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
if (mp != null) {
stop();
mp.release();
}
if (handler != null) {
handler.removeMessages(1);
handler = null;
}
if(notificationManager!=null){
notificationManager.cancel(notiID);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Song song=(Song) intent.getSerializableExtra("song");
if(null!=song&&song.getId()!=-1){
id=song.getId();
uri=Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, ""+id);
Log.i("playUri", uri.toString());
try {
//mp.reset();
mp.setDataSource(getBaseContext(), uri);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//显示通知栏
showMusicPlayerNotification(getResources().getString(R.string.app_name), notiID,
R.drawable.sweet, R.drawable.smiley_24,
song.getTitle(), song.getArtist());
}
int op=intent.getIntExtra("op", -1);
if(op!=-1){
MusicState ms=MusicState.values()[op];
Log.i("MusicState", op+"");
switch(ms){
case PLAYING:
play();
break;
case PAUSE:
pause();
break;
case STOP:
stop();
break;
case PROGRESS_CHANGE:
int progress=intent.getExtras().getInt("progress");
mp.seekTo(progress);
break;
}
}
return super.onStartCommand(intent, flags, startId);
}
// 播放音乐
public void play() {
setup();
init();
if(mp!=null){
mp.start();
}
}
// 暂停音乐
public void pause() {
//移除广播
handler.removeMessages(1);
if(mp!=null){
mp.pause();
}
}
// 停止音乐
public void stop() {
if(mp!=null){
mp.stop();
mp.reset();
}
//if(handler!=null) handler.removeMessages(1);
}
// 准备工作
public void setup() {
Intent intent = new Intent();
intent.setAction(MUSIC_DURATION);
try {
// if (!mp.isPlaying()) {
// mp.prepare();
// }
mp.prepare();
//mp.setWakeMode(getBaseContext(), PowerManager.PARTIAL_WAKE_LOCK);
mp.setScreenOnWhilePlaying(true);
mp.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// TODO Auto-generated method stub
handler.sendEmptyMessage(1);
}
});
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
duration=mp.getDuration();
intent.putExtra("duration", duration);
sendBroadcast(intent);
}
// 初始化服务
public void init() {
final Intent intent = new Intent();
intent.setAction(MUSIC_CURRENT);
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if (msg.what == 1) {
currentTime = mp.getCurrentPosition();
Log.i("currentTime", currentTime+"");
intent.putExtra("currentTime", currentTime);
sendBroadcast(intent);
}
handler.sendEmptyMessageDelayed(1, 800);
}
};
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
mp.reset();
Intent intent=new Intent();
intent.setAction(MUSIC_NEXT);
sendBroadcast(intent);
}
}
下一篇智能匹配本地歌词:http://blog.csdn.net/huweigoodboy/article/details/39862773