当前位置: 代码迷 >> Android >> 刚刚学android,练手写的一个播放器
  详细解决方案

刚刚学android,练手写的一个播放器

热度:75   发布时间:2016-05-01 19:20:19.0
刚学android,练手写的一个播放器

最近学习android开发做了一个播放器练手,同样是新手可以看看交流交流,呵呵,有什么更好的实现方法忘能指教一下

图效果在附件

主activity?? audioList.java

package com.xianyifa.audioplayer;import java.io.File;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import android.app.Activity;import android.app.AlertDialog;import android.app.Dialog;import android.content.ComponentName;import android.content.Context;import android.content.DialogInterface;import android.content.DialogInterface.OnClickListener;import android.content.Intent;import android.content.ServiceConnection;import android.graphics.Color;import android.media.MediaPlayer;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.util.DisplayMetrics;import android.util.Log;import android.view.ContextMenu;import android.view.ContextMenu.ContextMenuInfo;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuItem;import android.view.MenuItem.OnMenuItemClickListener;import android.view.View;import android.view.View.OnCreateContextMenuListener;import android.view.ViewGroup;import android.view.ViewGroup.LayoutParams;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.AdapterView.OnItemLongClickListener;import android.widget.ListView;import android.widget.SimpleAdapter;import android.widget.TextView;import android.widget.Toast;import com.xianyifa.audioplayer.impl.MyPlayer;import com.xianyifa.audioplayer.util.Toolbox;public class AudioList extends Activity {	private final String TAG = "AudioList";	private String filepath;//音乐文件绝对路径	private String longClickFilePath;//长按的音乐文件绝对路径	private int position = 0;//当前播放位置	private MyPlayer myPlayerService;//播放服务对象	private MyServiceConnection conn;	private Intent service;// 音乐播放服务意图	private String audioFile;// 音乐文件所在文件夹	private ListView listView;	private List<HashMap<String, Object>> data;// listView的数据	private long audioLength;// 播放音乐长度	private SimpleAdapter adapter;// ListView适配器	private int listId = -1;// 当前播放的音乐在listView的索引	private int onListId = -1;// 上一首播放的音乐在listView的索引	private MediaPlayer mediaPlayer;// 服务的播放器	private Handler handler;// 用于主线程和子线程的通讯	private ControlPlayTime controlPlayTime;// 音乐时间更新控制线程	private boolean controlPlayStop = false;//控制音乐时间更新控制线程结束	private boolean isStop = false;// 标识播放器是否暂停	private boolean isPause = false;// 标识activity是否是在暂停恢复	private int widthPixels;//设备屏幕宽度像素	private final int ADDAUDIOPLAYER_ID = Menu.FIRST;	private final int DELAUDIOPLAYER_ID = Menu.FIRST + 1;	private final int EXITAUDIOPLAYER_ID = Menu.FIRST + 2;	private final int DELETE_DIALOG = 1;	@Override	public void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.audiolist);		service = new Intent(this, PlayerService.class);// 通过意图取得播放服务		// 驱动服务,1、,2、激活后回返回一个通道,Activity和service通讯是通过通道通讯的,服务通道是一个接口要实现		// 3、常量1自动创建		conn = new MyServiceConnection();		this.startService(service);// 先使用创建服务在绑定		this.bindService(service, conn, BIND_AUTO_CREATE);				showListView();				//获取屏幕的宽带		DisplayMetrics dm = new DisplayMetrics();        getWindowManager().getDefaultDisplay().getMetrics(dm);        widthPixels = dm.widthPixels;				Log.i(TAG, "onCreate");	}	/*	 * 把数据绑定listview,并在界面显示	 */	private void showListView(){		Log.i(TAG, "showListView");		// 读取文件夹的音乐列表		// 判断是否存在SD卡		File file;		if (Environment.getExternalStorageState().equals(				Environment.MEDIA_MOUNTED)) {			audioFile = Environment.getExternalStorageDirectory() + "/myaudio";			file = new File(audioFile);			// 判断目录是否存在			if (!file.exists()) {				file.mkdirs();			}		} else {			audioFile = "/myaudio";			file = new File(audioFile);			// 判断目录是否存在			if (!file.exists()) {				file.mkdirs();			}		}		data = new ArrayList<HashMap<String, Object>>();		data = Toolbox.showCatalog(file);		// 取得listview		listView = (ListView) findViewById(R.id.audiolist);		adapter = new MyAdapter(AudioList.this, data,				R.layout.audiolistitem, new String[] { "filename", "filepath",						"playTime", "audioTime" }, new int[] { R.id.audioname,						R.id.audiopath, R.id.audioplaytime, R.id.audiotime });		listView.setAdapter(adapter);		// 创建播放控制线程		controlPlayTime = new ControlPlayTime();// 取得播放时间控制线程				// 创建线程通讯监听		handler = new Handler() {			@Override			public void handleMessage(Message msg) {				String message = (String) msg.obj;				//如果message是数字,就是发过来的歌曲长度,不是则是转换后的播放时间点				if(message.matches("[0-9]+")){					//这是服务在播放,用户重新回到activity界面时更新UI显示当前播放歌曲信息					HashMap<String, Object> item = (HashMap<String, Object>) listView.getItemAtPosition(listId);					item.put("audioTime", "/"+Toolbox.lengthTime(Long.parseLong(message)));				}else{					if (onListId == listId) {						HashMap<String, Object> item = (HashMap<String, Object>) listView.getItemAtPosition(listId);						item.put("playTime", message);					} else {						// 当是服务换歌是在这里更新UI显示						HashMap<String, Object> playItem = (HashMap<String, Object>) listView.getItemAtPosition(listId);						HashMap<String, Object> item = (HashMap<String, Object>) listView.getItemAtPosition(onListId);																		audioLength = myPlayerService.getPlayLength();						String time = Toolbox.lengthTime(audioLength);						playItem.put("audioTime", "/" + time);						playItem.put("playTime", "0:00");						item.put("audioTime", "");						item.put("playTime", "");						onListId = listId;//不要忘了这部要不然时间不会跳动					}				}				adapter.notifyDataSetChanged();				super.handleMessage(msg);			}		};				// 为listView注册单击事件		listView.setOnItemClickListener(new OnItemClickListener() {						@Override			public void onItemClick(AdapterView<?> parent, View view,					int position, long id) {				ListView v = (ListView) parent;				HashMap<String, Object> item = (HashMap<String, Object>) v.getItemAtPosition(position);				// 上个播放的文件在ListView的位置				HashMap<String, Object> Befitem = null;				if (listId > -1) {					Befitem = (HashMap<String, Object>) v.getItemAtPosition(listId);				}								listId = position;// 保存listView索引				onListId = listId;								filepath = item.get("filepath").toString();// 取得音乐路径				String playerFileName = myPlayerService.getFilePath();				if (mediaPlayer.isPlaying()) {// 如果正在播放					if (playerFileName.equals(filepath)) {// 而且请求的路径和现在播放的路径一样						myPlayerService.pause();// 暂停它						isStop = true;					} else {						try {							myPlayerService.play(filepath,									AudioList.this.position,listId);							audioLength = mediaPlayer.getDuration();							String time = Toolbox.lengthTime(audioLength);							item.put("audioTime", "/" + time);							item.put("playTime", "0:00");							if (!Befitem.isEmpty() || Befitem != null) {// 吧上个播放的文件总时间删除								Befitem.put("audioTime", "");								Befitem.put("playTime", "");							}							adapter.notifyDataSetChanged();// 让ListView更新						} catch (IOException e) {							Log.i(TAG, e.toString());						}					}				} else {// 如果不是在播放					if (isStop && playerFileName.equals(filepath)) {// 判断是不是停止状态并且请求播放的是同一个文件						myPlayerService.pause();					} else {// 不是暂停状态的调用播放,或者是暂停但是请求的不是同一个音乐文件						try {							myPlayerService.play(filepath,									AudioList.this.position,listId);							audioLength = mediaPlayer.getDuration();							String time = Toolbox.lengthTime(audioLength);							// 判断线程是否活动状态							if (!controlPlayTime.isAlive()) {								controlPlayTime.start();// 第一次执行播放开始线程							}														item.put("audioTime", "/" + time);							item.put("playTime", "0:00");							if (Befitem != null) {// 把上个播放的文件总时间删除,只有第一次播放和暂停换歌才会在这里掉用播放,只有暂停换歌才清空时间								Befitem.put("audioTime", "");								Befitem.put("playTime", "");							}														adapter.notifyDataSetChanged();// 让ListView更新						} catch (IOException e) {							Log.i(TAG, e.toString());						}					}				}			}		});				//为listview创建上文菜单		listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {						public void onCreateContextMenu(ContextMenu menu, View v,					ContextMenuInfo menuInfo) {				//设置图标				menu.setHeaderIcon(R.drawable.content_menu_ico);				//设置标题				menu.setHeaderTitle(R.string.choice_action);				//设置菜单				//播放				menu.add(R.string.player).setOnMenuItemClickListener(new OnMenuItemClickListener() {										@Override					public boolean onMenuItemClick(MenuItem item) {						// TODO Auto-generated method stub						return true;					}				});				//删除				menu.add(R.string.delete).setOnMenuItemClickListener(new OnMenuItemClickListener() {										@Override					public boolean onMenuItemClick(MenuItem item) {						showDialog(DELETE_DIALOG);//显示提示框						return true;					}				});							}		});				//为ListView创建一个item长按监听		listView.setOnItemLongClickListener(new OnItemLongClickListener() {			@Override			public boolean onItemLongClick(AdapterView<?> parent, View view,					int position, long id) {				ListView v = (ListView)parent;				HashMap<String, Object> item = (HashMap<String, Object>)v.getItemAtPosition(position);				longClickFilePath = item.get("filepath").toString();								listView.showContextMenu();				return true;			}					});			}			private class MyAdapter extends SimpleAdapter{				public MyAdapter(Context context, List<? extends Map<String, ?>> data,				int resource, String[] from, int[] to) {			super(context, data, resource, from, to);			// TODO Auto-generated constructor stub		}		/*		 * 每次加载listView都会调用		 * (non-Javadoc)		 * @see android.widget.SimpleAdapter#getView(int, android.view.View, android.view.ViewGroup)		 */		@Override		public View getView(int position, View convertView, ViewGroup parent) {			convertView = LayoutInflater.from(AudioList.this.getApplicationContext()).inflate(R.layout.audiolistitem, null);			TextView fileNameText = (TextView)convertView.findViewById(R.id.audioname);			TextView filePathText = (TextView)convertView.findViewById(R.id.audiopath);			TextView playTimeText = (TextView)convertView.findViewById(R.id.audioplaytime);			TextView fileTimeText = (TextView)convertView.findViewById(R.id.audiotime);			TextView progressBarText = (TextView)convertView.findViewById(R.id.progress_bar);			//比重新给值将显示空白?位置原因:getView就是把每天数据绑定到界面的过程,所以在这里要赋值			fileNameText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("filename").toString());			filePathText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("filepath").toString());			playTimeText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("playTime").toString());			fileTimeText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("audioTime").toString());			if(listId == position){				//他要求传int  但不能传颜色的十进制代码				fileNameText.setTextColor(Color.parseColor("#3197FF"));				fileNameText.setTextColor(Color.parseColor("#3197FF"));				playTimeText.setTextColor(Color.parseColor("#3197FF"));				fileTimeText.setTextColor(Color.parseColor("#3197FF"));				//修改进度条长度				LayoutParams laParaContent = (LayoutParams)progressBarText                                                                                   .getLayoutParams();				laParaContent.width = getprogressBarSize();				progressBarText.setLayoutParams(laParaContent);//				progressBarText.setWidth(0);//用这个更改不了			}else{				fileNameText.setTextColor(Color.parseColor("#000000"));			}			return convertView;		}			}		/*	 * 计算进度条的尺寸	 */	private int getprogressBarSize(){		double proportion = (double)mediaPlayer.getCurrentPosition()/(double)myPlayerService.getPlayLength();		int px = (int)(widthPixels * proportion);		return px;	}		/*	 * 因为系统内存不足被摧毁 (non-Javadoc)	 * 	 * @see android.app.Activity#onRestoreInstanceState(android.os.Bundle)	 */	@Override	protected void onRestoreInstanceState(Bundle savedInstanceState) {		this.position = savedInstanceState.getInt("position");		this.filepath = savedInstanceState.getString("filepath");		Log.i(TAG, "onRestoreInstanceState");		super.onRestoreInstanceState(savedInstanceState);	}	/*	 * 因为系统内存不足被摧毁 (non-Javadoc)	 * 	 * @see android.app.Activity#onSaveInstanceState(android.os.Bundle)	 */	@Override	protected void onSaveInstanceState(Bundle outState) {		outState.putInt("position", myPlayerService.getPosition());		outState.putString("filepath", myPlayerService.getFilePath());		Log.i(TAG, "onSaveInstanceState");		super.onSaveInstanceState(outState);	}	/*	 * 暂停了Activity (non-Javadoc)	 * 	 * @see android.app.Activity#onPause()	 */	@Override	protected void onPause() {		if (myPlayerService != null) {			myPlayerService.showNotification();			isPause = true;		}		Log.i(TAG, "onPause");		super.onPause();	}	/*	 * 重新唤起,或刚开启都会调用 (non-Javadoc)	 * 	 * @see android.app.Activity#onResume()	 */	@Override	protected void onResume() {		if (isPause && (myPlayerService != null)) {			myPlayerService.hideNotification();			isPause = false;		}		Log.i(TAG, "onResume");		super.onResume();	}	/*	 * 添加菜单 (non-Javadoc)	 * 	 * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)	 */	@Override	public boolean onCreateOptionsMenu(Menu menu) {		// TODO Auto-generated method stub		super.onCreateOptionsMenu(menu);		// 退出程序		menu.add(0, ADDAUDIOPLAYER_ID, 0, "添加歌曲").setShortcut('2', 'b');		menu.add(0, DELAUDIOPLAYER_ID, 0, "删除歌曲").setShortcut('2', 'b');		// .setIcon(R.drawable.exit);		// 退出程序		menu.add(0, EXITAUDIOPLAYER_ID, 0, "退出").setShortcut('4', 'd')				.setIcon(R.drawable.exit);		return true;	}	/*	 * 处理菜单动作 (non-Javadoc)	 * 	 * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)	 */	@Override	public boolean onOptionsItemSelected(MenuItem item) {		// TODO Auto-generated method stub		switch (item.getItemId()) {		case EXITAUDIOPLAYER_ID:			// 退出播放器			this.finish();//这里会执行解除绑定			controlPlayStop = true;//控制播放线程也结束			//等待播放控制线程结束才停止播放服务			while(controlPlayTime.isAlive()){				try {					Thread.sleep(1000);				} catch (InterruptedException e) {					// TODO Auto-generated catch block					Log.e(TAG, e.toString());				}				Log.i(TAG, "wait controlPlayTime stop");			}			this.stopService(service);			return true;		}		return super.onOptionsItemSelected(item);	}	/*	 * 实现通道	 */	private final class MyServiceConnection implements ServiceConnection {		/*		 * 链接服务调用方法 service 为binder 通讯的桥梁 (non-Javadoc)		 * 		 * @see		 * android.content.ServiceConnection#onServiceConnected(android.content		 * .ComponentName, android.os.IBinder)		 */		@Override		public void onServiceConnected(ComponentName name, IBinder service) {			// Binder binder = (Binder)service;			myPlayerService = (MyPlayer) service;			myPlayerService.setListViewData(data);			listId = myPlayerService.getListId();			onListId = listId;//当前播放歌曲的listView数据索引						mediaPlayer = myPlayerService.getMediaPlayer();// 取得服务中的播放器			// 判断线程是否活动状态,并且音乐服务在播放  在这里就启动更新时间线程			if (myPlayerService.getIsPlayInit()) {				audioLength = myPlayerService.getPlayLength();//当前播放歌曲的长度				controlPlayTime.start();// 第一次执行播放开始线程			}			Log.i(TAG, "onServiceConnected");		}		/*		 * 断开服务调用方法 (non-Javadoc)		 * 		 * @see		 * android.content.ServiceConnection#onServiceDisconnected(android.content		 * .ComponentName)		 */		@Override		public void onServiceDisconnected(ComponentName name) {			myPlayerService = null;			Log.i(TAG, "onServiceDisconnected");		}	}	/*	 * 播放时间更新控制线程,只有播放器存在才会启动	 */	public class ControlPlayTime extends Thread {		@Override		public void run() {			//线程刚启动就发给handler让他更新UI播放的音乐总长度			Message message1 = Message.obtain();			message1.obj = audioLength+"";//audioLength是long行要转换字符串传递			handler.sendMessage(message1);			// 判断歌曲是否还在播放				while (!controlPlayStop) {					long milliSecond = mediaPlayer.getCurrentPosition();					String time = Toolbox.formatTime(milliSecond);					Message message = Message.obtain();// 用于和住线程通讯的消息容器,每次通讯都要创建					message.obj = time;					handler.sendMessage(message);										if((audioLength - milliSecond <= 1100)){						Log.i(TAG, "waiting next song");						while(audioLength == myPlayerService.getPlayLength()){							milliSecond = mediaPlayer.getCurrentPosition();							time = Toolbox.formatTime(milliSecond);							Message message2 = Message.obtain();// 用于和住线程通讯的消息容器,每次通讯都要创建							message2.obj = time;							handler.sendMessage(message2);							Log.i(TAG, "ControlPlayTime waiting next song");							try {								Thread.sleep(50);							} catch (InterruptedException e) {								Log.i(TAG, e.toString());							}						}						Log.i(TAG, "ControlPlayTime star updata next song playtime");						listId = myPlayerService.getListId();						//立即向handler发一个消息,这个消息内容没什么意义,只是使handler为下一首歌曲界面显示初始化						Message message3 = Message.obtain();// 用于和住线程通讯的消息容器,每次通讯都要创建						message3.obj = "0:00";						handler.sendMessage(message3);					}					try {						Thread.sleep(1000);					} catch (InterruptedException e) {						Log.e(TAG, e.toString());					}										}		}	}		/*	 * 创建弹出确认窗口	 * (non-Javadoc)	 * @see android.app.Activity#onCreateDialog(int, android.os.Bundle)	 */	@Override	protected Dialog onCreateDialog(int id) {		switch (id) {		case DELETE_DIALOG:			return new AlertDialog.Builder(AudioList.this)			.setTitle(R.string.prompt).setMessage(getString(R.string.verify_del)					+"\""+ longClickFilePath.substring(longClickFilePath.lastIndexOf("/")+1, longClickFilePath.length())+"\"?")			.setPositiveButton(R.string.verify, new OnClickListener() {								@Override				public void onClick(DialogInterface dialog, int which) {					//确定执行操作										removeDialog(DELETE_DIALOG);//吧创建的弹出删除,不删除下次创建还是同一个对象,导致消息内容不变				}			}).setNegativeButton(R.string.cancel, new OnClickListener() {								@Override				public void onClick(DialogInterface dialog, int which) {					removeDialog(DELETE_DIALOG);				}			}).create();		default:			return null;		}	}		@Override	protected void onDestroy() {		controlPlayStop = true;//控制播放线程也结束		//等待播放控制线程结束才停止播放服务		while(controlPlayTime.isAlive()){			try {				Thread.sleep(1000);			} catch (InterruptedException e) {				// TODO Auto-generated catch block				Log.e(TAG, e.toString());			}			Log.i(TAG, "wait controlPlayTmie stop");		}		unbindService(conn);		Log.i(TAG, "Activity onDestroy");		super.onDestroy();	}}

?控制播放的服务PlayerService.java

package com.xianyifa.audioplayer;import java.io.File;import java.io.IOException;import java.util.HashMap;import java.util.List;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.app.Service;import android.content.Context;import android.content.Intent;import android.graphics.Color;import android.media.MediaPlayer;import android.os.Binder;import android.os.IBinder;import android.telephony.PhoneStateListener;import android.telephony.TelephonyManager;import android.util.Log;import com.xianyifa.audioplayer.impl.MyPlayer;public class PlayerService extends Service {	private final String TAG = "PlayerService";    private MediaPlayer mediaPlayer;//实例化一个播放器;    private String filepath = null;//文件绝对路径        private int position;//播放的进度    private long playLength;//正在播放音乐的长度    private boolean isStop = false;    private boolean controlPlayStop = false;//播放否控制    private boolean isPlayInit = false;//播放器是否初始化    private List<HashMap<String, Object>> listViewData;// listView的数据     private ControlPlay controlPlay;//播放控制线程    private int listId = -1;    private boolean isShowNotification = false;    private Binder binder = new MyBinder();//创建一个通讯,用于返回给调用,建立通讯桥梁,通讯都基于次桥梁	@Override	public IBinder onBind(Intent arg0) {		// 取得电话服务,实现电话进来的时候停止播放,挂断的继续		TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);		// 注册监听,监听电话状态,并指定触发后执行的类,1、调用监听处理类方法,2、监听到的通讯状态(电话进入,接通电话,挂断电话)		telManager.listen(new TelListenr(),				PhoneStateListener.LISTEN_CALL_STATE);		mediaPlayer = new MediaPlayer();//实例化一个播放器;		controlPlay = new ControlPlay();				return binder;	}		public void setFilePath(String filepath){		this.filepath = filepath;	}		public void setPosition(int position){		this.position = position;	}			/*	 * 为Binder添加业务方法,只有在这里面才能通过Binder建立的通道进行调用	 */	private final class MyBinder extends Binder implements MyPlayer{				public void setFilePath(String filepath){			PlayerService.this.setFilePath(filepath);		}				public void setListViewData(List<HashMap<String, Object>> listViewData){			PlayerService.this.listViewData = listViewData;		}				public void play(String filepath,int position,int id) throws IOException{			setFilePath(filepath);//在服务保存当前MP3路径			setPosition(position);//在服务保存当前MP3路径			listId = id;			PlayerService.this.isStop = false;			File file = new File(filepath);	    	mediaPlayer.reset();//把之前的设置都重置一下	    	mediaPlayer.setDataSource(file.getAbsolutePath());//设置音乐文件路径	    	mediaPlayer.prepare();//缓存一定要调用,初始化	    	mediaPlayer.start();	    	mediaPlayer.seekTo(position);	    	isPlayInit = true;	    	playLength = mediaPlayer.getDuration();	    	if(!PlayerService.this.controlPlay.isAlive()){	    		controlPlay.start();	    	}		}				public boolean pause(){			if(mediaPlayer.isPlaying()){//如果是在播放				mediaPlayer.pause();				return true;			}else{				//应为如果按了停止直接使用start 继续叫报错				if(!PlayerService.this.isStop){					mediaPlayer.start();				}				return false;			}		}				public void reset()throws IOException{			if(mediaPlayer.isPlaying()){				mediaPlayer.seekTo(0);			}else if(PlayerService.this.filepath != null){//确保用户先点击过播放				play(PlayerService.this.filepath,0,listId);			}		}				public void stop(){			if(mediaPlayer.isPlaying()){				mediaPlayer.stop();				PlayerService.this.isStop = true;			}		}				public int getPosition(){			return mediaPlayer.getCurrentPosition();		}				public String getFilePath(){			return PlayerService.this.filepath;		}				public MediaPlayer getMediaPlayer(){			return mediaPlayer;		}				public int getListId(){			return listId;		}				public boolean getIsPlayInit(){			return isPlayInit;		}				public long getPlayLength(){			return playLength;		}		/*		 * 显示通知栏图标,当界面不可见的时候调用		 * (non-Javadoc)		 * @see com.xianyifa.audioplayer.impl.MyPlayer#showNotification()		 */		public void showNotification(){			// 创建一个NotificationManager的引用	        NotificationManager notificationManager = (NotificationManager)	            PlayerService.this.getSystemService(android.content.Context.NOTIFICATION_SERVICE);	       	        // 定义Notification的各种属性	        Notification notification =new Notification(R.drawable.icon,	               (filepath != null) ? filepath.substring(filepath.lastIndexOf("/")+1, filepath.length()) : "无音乐播放", System.currentTimeMillis());	        notification.flags |= Notification.FLAG_ONGOING_EVENT; // 将此通知放到通知栏的"Ongoing"即"正在运行"组中	        notification.flags |= Notification.FLAG_NO_CLEAR; // 表明在点击了通知栏中的"清除通知"后,此通知不清除,经常与FLAG_ONGOING_EVENT一起使用	        notification.flags |= Notification.FLAG_SHOW_LIGHTS;	        notification.defaults = Notification.DEFAULT_LIGHTS;	        notification.ledARGB = Color.BLUE;	        notification.ledOnMS =5000;	               	        // 设置通知的事件消息	        CharSequence contentTitle = "正在播放……"; // 通知栏标题	        CharSequence contentText = (filepath != null) 	        		? filepath.substring(filepath.lastIndexOf("/")+1, filepath.length()) :"无音乐播放"; // 通知栏内容//	        		CharSequence contentText = "无音乐播放"; // 通知栏内容	        Intent notificationIntent = new Intent(PlayerService.this, AudioList.class); // 点击该通知后要跳转的Activity	        //添加这里可以解决当按home键停止activity在冲通知进入时出现多个activity对象	        //也就是再按返回是跳到另一个还是这个界面的activity	        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  	        PendingIntent contentItent = PendingIntent.getActivity(PlayerService.this, 0,	                notificationIntent, 0);	        notification.setLatestEventInfo(PlayerService.this, contentTitle, contentText,	                contentItent);	        	        	        // 把Notification传递给NotificationManager	        notificationManager.notify(0, notification);	        isShowNotification = true;		}				/*		 * 删除通知栏的图标		 * (non-Javadoc)		 * @see com.xianyifa.audioplayer.impl.MyPlayer#hideNotification()		 */		public void hideNotification(){			// 启动后删除之前我们定义的通知				NotificationManager notificationManager = (NotificationManager) PlayerService.this                .getSystemService(NOTIFICATION_SERVICE);				notificationManager.cancel(0);				isShowNotification = false;		}	}		/*	 * 实现电话状态类	 */	private class TelListenr extends PhoneStateListener {		@Override		public void onCallStateChanged(int state, String incomingNumber) {			try {				switch (state) {				case TelephonyManager.CALL_STATE_IDLE:// 挂断					PlayerService.this.mediaPlayer.start();					break;				case TelephonyManager.CALL_STATE_OFFHOOK:// 接通电话										break;				case TelephonyManager.CALL_STATE_RINGING:// 电话进入					if(PlayerService.this.mediaPlayer.isPlaying()){//如果是在播放						PlayerService.this.mediaPlayer.pause();//暂停					}					break;				default:					break;				}			} catch (Exception e) {				// TODO Auto-generated catch block				Log.i(TAG, e.toString());			}			super.onCallStateChanged(state, incomingNumber);		}	}	/*	 * 播放控制线程	 */	public class ControlPlay extends Thread {		@Override		public void run() {				while (!controlPlayStop) {					try {						Thread.sleep(800);					} catch (InterruptedException e) {						Log.e(TAG, e.toString());					}					long milliSecond = mediaPlayer.getCurrentPosition();					if((playLength - milliSecond) <= 1100){//						String filepath = filename;						String audioName = filepath.substring(filepath.lastIndexOf("/")+1,                                                                                  filepath.length());						//第一次换歌没有初始化,先找到当前在listView的索引以后往上加就可以知道列表末尾						if(listId == -1){							listId = getListId(audioName, filepath);						}						if(listId < (listViewData.size() - 1)){							listId += 1;						}else{							listId = 0;						}						String playPath = getFilePath(listId);						Log.i(TAG, listId+"------"+playPath+"----"+listViewData.size());						MyPlayer myPlayer = (MyBinder)binder;						try {							Log.i(TAG, "service waiting 3 Second play next song");							Thread.sleep(3000);							myPlayer.play(playPath, 0,listId);						} catch (Exception e) {							Log.e(TAG, e.toString());						}						//判断通知是不是显示,做出对通知信息的更改						if(isShowNotification){							myPlayer.hideNotification();							myPlayer.showNotification();						}					}				}		}	}		/*	 * data list绑定的数据	 * filepath 正在播放歌曲的路径	 * filename 正在播放歌曲名称	 * return i 返回正在播放的歌曲在listView绑定数据的索引	 */	private int getListId(String filename,String filepath){		int i = 0;		for(HashMap<String,Object> audio : listViewData){			if(audio.get("filepath").equals(filepath)){				break;			}			i++;		}		return i;	}	/*	 * index 歌曲在listView绑定数据的索引	 * return 返回歌曲的绝对路径	 */	private String getFilePath(int index){		HashMap<String,Object> audio = listViewData.get(index);		return audio.get("filepath").toString();	}		@Override	public void onDestroy() {		controlPlayStop = true;//控制播放线程也结束		//等待播放控制线程结束才停止播放服务		while(controlPlay.isAlive()){			try {				Thread.sleep(1000);			} catch (InterruptedException e) {				Log.e(TAG, e.toString());			}			Log.i(TAG, "wait controlPlay stop");		}		//播放控制线程停止后在停止播放器		if(mediaPlayer != null){			MyPlayer myPlayer = (MyBinder)binder;			myPlayer.hideNotification();//服务结束的时候一定要清楚通知栏			mediaPlayer.stop();//一定要在这里停止,要不然服务停止了播放器还是会继续播放			mediaPlayer.release();//释放资源		}		super.onDestroy();	}	}

?实现服务和activity通讯的binder继承类的接口

package com.xianyifa.audioplayer.impl;import java.io.IOException;import java.util.HashMap;import java.util.List;import android.media.MediaPlayer;import android.os.Handler;public interface MyPlayer {	public void play(String filename,int position,int id) throws IOException;		public boolean pause();		public void reset() throws IOException;		public void stop();		public void setFilePath(String filepath);		public void showNotification();		public void hideNotification();		public int getPosition();		public String getFilePath();		public MediaPlayer getMediaPlayer();		public void setListViewData(List<HashMap<String, Object>> listViewData);		public int getListId();		public boolean getIsPlayInit();		public long getPlayLength();}

?主界面XMLaudiolist.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:background="@android:color/white"     >	    <ListView         android:layout_width="fill_parent"    	android:layout_height="fill_parent"    	android:id="@+id/audiolist"    	android:cacheColorHint="#00000000"        >            </ListView>    </LinearLayout>

?item.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:orientation="vertical"    android:id="@+id/listviewitem"    >	<LinearLayout	    android:layout_width="fill_parent"	    android:layout_height="40dip"	    android:orientation="horizontal"    	>	    <TextView 	        android:layout_width="wrap_content"	    	android:layout_height="wrap_content"	        android:id="@+id/audioname"	        android:layout_marginLeft="20dip"	        android:layout_marginTop="10dip"	        android:textColor="@android:color/black"	        />	    <LinearLayout		    android:layout_width="fill_parent"		    android:layout_height="fill_parent"		    android:orientation="horizontal"		    android:layout_marginTop="10dip"		    android:layout_marginRight="20dip"		    android:gravity="right">		    <TextView 		        android:layout_width="wrap_content"		    	android:layout_height="wrap_content"		        android:id="@+id/audioplaytime"		        android:layout_gravity="right"		        android:textColor="@android:color/black"		        />		    <TextView 		        android:layout_width="wrap_content"		    	android:layout_height="wrap_content"		        android:id="@+id/audiotime"		        android:textColor="@android:color/black"		        />	    </LinearLayout>	    <TextView 	        android:layout_width="0dip"	    	android:layout_height="0dip"	        android:id="@+id/audiopath"	        />	</LinearLayout>	<LinearLayout	    android:layout_width="fill_parent"	    android:layout_height="1dip"	    android:orientation="horizontal"    	>	    <TextView 	        android:layout_width="0px"	    	android:layout_height="fill_parent"	    	android:id="@+id/progress_bar"	    	android:background="@android:color/black"	        />	 </LinearLayout></LinearLayout>工具类Toolbox .javapackage com.xianyifa.audioplayer.util;import java.io.File;import java.util.ArrayList;import java.util.HashMap;import java.util.List;public class Toolbox {	// 调用文件目录查询方法    //方法一;以绝对路径输出给定的目录下的所有文件路径    public static List<HashMap<String, Object>> showCatalog(File file) {        //System.out.println(file.getName());    	List<HashMap<String, Object>> fileList = new ArrayList<HashMap<String, Object>>();        File[] files = file.listFiles();        if (files != null) {            for (File f : files) {                if (f.isDirectory()) {//判断是否是目录                    showCatalog(f);                } else {//                    System.out.println(f.getName());//输出文件或文件夹名称                    if(f.getName().substring(f.getName().lastIndexOf("."), f.getName().length()).equals(".mp3")){                    	HashMap<String, Object> item = new HashMap<String, Object>();                    	item.put("filepath",f.getAbsolutePath());//输出绝对路径                    	item.put("filename",f.getName());//输出绝对路径                    	item.put("playTime","");//输出绝对路径                    	item.put("audioTime","");//输出绝对路径                    	fileList.add(item);                    }                }            }        }        return fileList;    }    /*     * 格式化当前播放时间点时间     */    public static String formatTime(long milliSecond){    	int minute = (int)(milliSecond/1000)/60;    	int second = (int)(milliSecond/1000)%60;		String sec = second+"";		if(second<10){			sec = "0"+second;		}		String time = minute+":"+sec;    	return time;    }        /*     * 求总时长     */    public static String lengthTime(long milliSecond){    	int minute = (int)(milliSecond/1000)/60;    	int second = (int)(milliSecond/1000)%60;    	if(milliSecond%1000 > 500){    		second += 1;    		if(second == 60){    			minute += 1;    			second = 0;    		}    	}		String sec = second+"";		if(second<10){			sec = "0"+second;		}		String time = minute+":"+sec;    	return time;    }}?                                                                                                                                                                           

?

?

?

新上传的源码修改了一些小地方,服务播放变更改用广播通知activity;感觉用这个比较好,有利以后实现歌词同步。有空在研究实现歌词同步
  相关解决方案