android音乐播放器开发 SweetMusicPlayer 播放本地音乐

上一篇写了载入歌曲列表,http://blog.csdn.net/huweigoodboy/article/details/39856411,如今来总结下播放本地音乐。

一,MediaPlayer

android音乐播放器开发 SweetMusicPlayer 播放本地音乐

首先来看看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。

二,控制播放的service

为什么不用activity呢?假设切到后台,activity就会被销毁。播放就会出现各种问题。

这里设置各种状态,如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

上一篇:java 获取键盘输入常用的两种方法


下一篇:spring mvc静态资源文件的引用