最近学习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> 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;@Overridepublic 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>();data = Toolbox.showCatalog(file);// 取得listviewlistView = (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() {@Overridepublic void handleMessage(Message msg) {String message = (String) msg.obj;//如果message是数字,就是发过来的歌曲长度,不是则是转换后的播放时间点if(message.matches("[0-9]+")){//这是服务在播放,用户重新回到activity界面时更新UI显示当前播放歌曲信息HashMap item = (HashMap) listView.getItemAtPosition(listId);item.put("audioTime", "/"+Toolbox.lengthTime(Long.parseLong(message)));}else{if (onListId == listId) {HashMap item = (HashMap) listView.getItemAtPosition(listId);item.put("playTime", message);} else {// 当是服务换歌是在这里更新UI显示HashMap playItem = (HashMap) listView.getItemAtPosition(listId);HashMap item = (HashMap) 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() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {ListView v = (ListView) parent;HashMap item = (HashMap) v.getItemAtPosition(position);// 上个播放的文件在ListView的位置HashMap Befitem = null;if (listId > -1) {Befitem = (HashMap) 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() {@Overridepublic boolean onMenuItemClick(MenuItem item) {// TODO Auto-generated method stubreturn true;}});//删除menu.add(R.string.delete).setOnMenuItemClickListener(new OnMenuItemClickListener() {@Overridepublic boolean onMenuItemClick(MenuItem item) {showDialog(DELETE_DIALOG);//显示提示框return true;}});}});//为ListView创建一个item长按监听listView.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view,int position, long id) {ListView v = (ListView)parent;HashMap item = (HashMap)v.getItemAtPosition(position);longClickFilePath = item.get("filepath").toString();listView.showContextMenu();return true;}});}private class MyAdapter extends SimpleAdapter{public MyAdapter(Context context, List<? extends Map> 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) */@Overridepublic 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)listView.getItemAtPosition(position)).get("filename").toString());filePathText.setText(((HashMap)listView.getItemAtPosition(position)).get("filepath").toString());playTimeText.setText(((HashMap)listView.getItemAtPosition(position)).get("playTime").toString());fileTimeText.setText(((HashMap)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) */@Overrideprotected 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) */@Overrideprotected 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() */@Overrideprotected void onPause() {if (myPlayerService != null) {myPlayerService.showNotification();isPause = true;}Log.i(TAG, "onPause");super.onPause();}/* * 重新唤起,或刚开启都会调用 (non-Javadoc) *  * @see android.app.Activity#onResume() */@Overrideprotected 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) */@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// TODO Auto-generated method stubsuper.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) */@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// TODO Auto-generated method stubswitch (item.getItemId()) {case EXITAUDIOPLAYER_ID:// 退出播放器this.finish();//这里会执行解除绑定controlPlayStop = true;//控制播放线程也结束//等待播放控制线程结束才停止播放服务while(controlPlayTime.isAlive()){try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blockLog.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) */@Overridepublic 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) */@Overridepublic void onServiceDisconnected(ComponentName name) {myPlayerService = null;Log.i(TAG, "onServiceDisconnected");}}/* * 播放时间更新控制线程,只有播放器存在才会启动 */public class ControlPlayTime extends Thread {@Overridepublic 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) */@Overrideprotected 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() {@Overridepublic void onClick(DialogInterface dialog, int which) {//确定执行操作removeDialog(DELETE_DIALOG);//吧创建的弹出删除,不删除下次创建还是同一个对象,导致消息内容不变}}).setNegativeButton(R.string.cancel, new OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {removeDialog(DELETE_DIALOG);}}).create();default:return null;}}@Overrideprotected void onDestroy() {controlPlayStop = true;//控制播放线程也结束//等待播放控制线程结束才停止播放服务while(controlPlayTime.isAlive()){try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blockLog.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> listViewData;// listView的数据     private ControlPlay controlPlay;//播放控制线程    private int listId = -1;    private boolean isShowNotification = false;    private Binder binder = new MyBinder();//创建一个通讯,用于返回给调用,建立通讯桥梁,通讯都基于次桥梁@Overridepublic 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> 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 {@Overridepublic 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 blockLog.i(TAG, e.toString());}super.onCallStateChanged(state, incomingNumber);}}/* * 播放控制线程 */public class ControlPlay extends Thread {@Overridepublic 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 audio : listViewData){if(audio.get("filepath").equals(filepath)){break;}i++;}return i;}/* * index 歌曲在listView绑定数据的索引 * return 返回歌曲的绝对路径 */private String getFilePath(int index){HashMap audio = listViewData.get(index);return audio.get("filepath").toString();}@Overridepublic 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> listViewData);public int getListId();public boolean getIsPlayInit();public long getPlayLength();}

 主界面XMLaudiolist.xml

<?xml version="1.0" encoding="utf-8"?>                    

 item.xml

<?xml version="1.0" encoding="utf-8"?>                             工具类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> showCatalog(File file) {        //System.out.println(file.getName());    List> fileList = new ArrayList>();        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 item = new HashMap();                    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;感觉用这个比较好,有利以后实现歌词同步。有空在研究实现歌词同步

 

 

 

更多相关文章

  1. 实例教程十:监听ContentProvider中数据的变化
  2. 转:android笔记之contacts(通讯录)数据库
  3. Android(安卓)顶级视图DecorView的前世今生
  4. android通过自定义toast实现悬浮通知效果的示例代码
  5. Fragment Management
  6. 【android】binder机制-servicemanager
  7. 关于android语言切换后通知栏显示的问题
  8. Android快速实现断点续传的方法
  9. 连接服务器超时的Demo

随机推荐

  1. Android市场将持续爆发
  2. [Android(安卓)JNI] JNI Types and Data
  3. android加密的即时通信软件 -客户端
  4. Android注解:自定义注解之源码注解
  5. 「Android」Activity的工作过程
  6. Android(安卓)clipChildren用法
  7. android环境变量的设置及注意问题
  8. Android(安卓)用网络图片做帧动画
  9. 搭建android的开发环境
  10. android:layout_gravity 和 android:grav