Android应用开发--MP3音乐播放器Service实现

Android应用开发--MP3音乐播放器Service实现
2013年5月29日简、美音乐播放器开发记录

让网友们久等啦,关于简、美音乐播放器的开发,最重要的Service类总算是要发博了。关于Android五大组件之一的Service在音乐播放器开发中得到了很好的应用,不仅是Service,广播(Broadcast)、Activity、Content Provider都会在此次开发中用到。所以说对于Android的一个很好的练手项目,音乐播放器是毋庸置疑的。上一篇,主要介绍的是播放界面的业务逻辑实现,不过这些业务逻辑都是以Service为中心的。

  
package com.wwj.sb.service;

import java.util.List;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

import com.wwj.sb.activity.PlayerActivity;
import com.wwj.sb.domain.AppConstant;
import com.wwj.sb.domain.Mp3Info;
import com.wwj.sb.utils.MediaUtil;
/***
 * 2013/5/25
 * @author wwj
 * 音乐播放服务
 */
@SuppressLint("NewApi")
public class PlayerService extends Service {
	private MediaPlayer mediaPlayer; // 媒体播放器对象
	private String path; 			// 音乐文件路径
	private int msg;
	private boolean isPause; 		// 暂停状态
	private int current = 0; 		// 记录当前正在播放的音乐
	private List<Mp3Info> mp3Infos;	//存放Mp3Info对象的集合
	private int status = 3;			//播放状态,默认为顺序播放
	private MyReceiver myReceiver;	//自定义广播接收器
	private int currentTime;		//当前播放进度
	private int duration;			//播放长度
	
	//服务要发送的一些Action
	public static final String UPDATE_ACTION = "com.wwj.action.UPDATE_ACTION";	//更新动作
	public static final String CTL_ACTION = "com.wwj.action.CTL_ACTION";		//控制动作
	public static final String MUSIC_CURRENT = "com.wwj.action.MUSIC_CURRENT";	//当前音乐播放时间更新动作
	public static final String MUSIC_DURATION = "com.wwj.action.MUSIC_DURATION";//新音乐长度更新动作
	
	/**
	 * handler用来接收消息,来发送广播更新播放时间
	 */
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			if (msg.what == 1) {
				if(mediaPlayer != null) {
					currentTime = mediaPlayer.getCurrentPosition(); // 获取当前音乐播放的位置
					Intent intent = new Intent();
					intent.setAction(MUSIC_CURRENT);
					intent.putExtra("currentTime", currentTime);
					sendBroadcast(intent); // 给PlayerActivity发送广播
					handler.sendEmptyMessageDelayed(1, 1000);
				}
				
			}
		};
	};

	@Override
	public void onCreate() {
		super.onCreate();
		Log.d("service", "service created");
		mediaPlayer = new MediaPlayer();
		mp3Infos = MediaUtil.getMp3Infos(PlayerService.this);
		

		/**
		 * 设置音乐播放完成时的监听器
		 */
		mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

			@Override
			public void onCompletion(MediaPlayer mp) {
				if (status == 1) { // 单曲循环
					mediaPlayer.start();
				} else if (status == 2) { // 全部循环
					current++;
					if(current > mp3Infos.size() - 1) {	//变为第一首的位置继续播放
						current = 0;
					}
					Intent sendIntent = new Intent(UPDATE_ACTION);
					sendIntent.putExtra("current", current);
					// 发送广播,将被Activity组件中的BroadcastReceiver接收到
					sendBroadcast(sendIntent);
					path = mp3Infos.get(current).getUrl();
					play(0);
				} else if (status == 3) { // 顺序播放
					current++;	//下一首位置
					if (current <= mp3Infos.size() - 1) {
						Intent sendIntent = new Intent(UPDATE_ACTION);
						sendIntent.putExtra("current", current);
						// 发送广播,将被Activity组件中的BroadcastReceiver接收到
						sendBroadcast(sendIntent);
						path = mp3Infos.get(current).getUrl();
						play(0);
					}else {
						mediaPlayer.seekTo(0);
						current = 0;
						Intent sendIntent = new Intent(UPDATE_ACTION);
						sendIntent.putExtra("current", current);
						// 发送广播,将被Activity组件中的BroadcastReceiver接收到
						sendBroadcast(sendIntent);
					}
				} else if(status == 4) {	//随机播放
					current = getRandomIndex(mp3Infos.size() - 1);
					System.out.println("currentIndex ->" + current);
					Intent sendIntent = new Intent(UPDATE_ACTION);
					sendIntent.putExtra("current", current);
					// 发送广播,将被Activity组件中的BroadcastReceiver接收到
					sendBroadcast(sendIntent);
					path = mp3Infos.get(current).getUrl();
					play(0);
				}
			}
		});

		myReceiver = new MyReceiver();
		IntentFilter filter = new IntentFilter();
		filter.addAction(PlayerActivity.CTL_ACTION);
		registerReceiver(myReceiver, filter);
	}

	/**
	 * 获取随机位置
	 * @param end
	 * @return
	 */
	protected int getRandomIndex(int end) {
		int index = (int) (Math.random() * end);
		return index;
	}

	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}

	@Override
	public void onStart(Intent intent, int startId) {
		path = intent.getStringExtra("url");		//歌曲路径
		current = intent.getIntExtra("listPosition", -1);	//当前播放歌曲的在mp3Infos的位置
		msg = intent.getIntExtra("MSG", 0);			//播放信息
		if (msg == AppConstant.PlayerMsg.PLAY_MSG) {	//直接播放音乐
			play(0);
		} else if (msg == AppConstant.PlayerMsg.PAUSE_MSG) {	//暂停
			pause();	
		} else if (msg == AppConstant.PlayerMsg.STOP_MSG) {		//停止
			stop();
		} else if (msg == AppConstant.PlayerMsg.CONTINUE_MSG) {	//继续播放
			resume();	
		} else if (msg == AppConstant.PlayerMsg.PRIVIOUS_MSG) {	//上一首
			previous();
		} else if (msg == AppConstant.PlayerMsg.NEXT_MSG) {		//下一首
			next();
		} else if (msg == AppConstant.PlayerMsg.PROGRESS_CHANGE) {	//进度更新
			currentTime = intent.getIntExtra("progress", -1);
			play(currentTime);
		} else if (msg == AppConstant.PlayerMsg.PLAYING_MSG) {
			handler.sendEmptyMessage(1);
		}
		super.onStart(intent, startId);
	}

	/**
	 * 播放音乐
	 * 
	 * @param position
	 */
	private void play(int currentTime) {
		try {
			mediaPlayer.reset();// 把各项参数恢复到初始状态
			mediaPlayer.setDataSource(path);
			mediaPlayer.prepare(); // 进行缓冲
			mediaPlayer.setOnPreparedListener(new PreparedListener(currentTime));// 注册一个监听器
			handler.sendEmptyMessage(1);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 暂停音乐
	 */
	private void pause() {
		if (mediaPlayer != null && mediaPlayer.isPlaying()) {
			mediaPlayer.pause();
			isPause = true;
		}
	}

	private void resume() {
		if (isPause) {
			mediaPlayer.start();
			isPause = false;
		}
	}

	/**
	 * 上一首
	 */
	private void previous() {
		Intent sendIntent = new Intent(UPDATE_ACTION);
		sendIntent.putExtra("current", current);
		// 发送广播,将被Activity组件中的BroadcastReceiver接收到
		sendBroadcast(sendIntent);
		play(0);
	}

	/**
	 * 下一首
	 */
	private void next() {
		Intent sendIntent = new Intent(UPDATE_ACTION);
		sendIntent.putExtra("current", current);
		// 发送广播,将被Activity组件中的BroadcastReceiver接收到
		sendBroadcast(sendIntent);
		play(0);
	}

	/**
	 * 停止音乐
	 */
	private void stop() {
		if (mediaPlayer != null) {
			mediaPlayer.stop();
			try {
				mediaPlayer.prepare(); // 在调用stop后如果需要再次通过start进行播放,需要之前调用prepare函数
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void onDestroy() {
		if (mediaPlayer != null) {
			mediaPlayer.stop();
			mediaPlayer.release();
			mediaPlayer = null;
		}
		
	}

	/**
	 * 
	 * 实现一个OnPrepareLister接口,当音乐准备好的时候开始播放
	 * 
	 */
	private final class PreparedListener implements OnPreparedListener {
		private int currentTime;

		public PreparedListener(int currentTime) {
			this.currentTime = currentTime;
		}

		@Override
		public void onPrepared(MediaPlayer mp) {
			mediaPlayer.start(); // 开始播放
			if (currentTime > 0) { // 如果音乐不是从头播放
				mediaPlayer.seekTo(currentTime);
			}
			Intent intent = new Intent();
			intent.setAction(MUSIC_DURATION);
			duration = mediaPlayer.getDuration();
			intent.putExtra("duration", duration);	//通过Intent来传递歌曲的总长度
			sendBroadcast(intent);
		}
	}

	public class MyReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			int control = intent.getIntExtra("control", -1);
			switch (control) {
			case 1:
				status = 1; // 将播放状态置为1表示:单曲循环
				break;
			case 2:
				status = 2;	//将播放状态置为2表示:全部循环
				break;
			case 3:
				status = 3;	//将播放状态置为3表示:顺序播放
				break;
			case 4:
				status = 4;	//将播放状态置为4表示:随机播放
				break;
			}
		}
	}

}

以上的代码要注意的是在Service在被调用startService()方法会在回调onStart(),服务是不会被多次创建的,但会多次调用onStart方法,然而通过Intent传过来的数据,也只能在onStart方法内来接收。
似乎关于Service类没什么可说的,简单来说服务是我们看不见的东西,只是在后台不断运行的程序,音乐播放通过服务来实现,才不会在界面不可见的时候停掉,这是音乐播放器使用Service的原因。
第一阶段的音乐播放器开发已经到现在全部介绍完,第二阶段会实现歌词的显示和网络相关的下载和搜索,在这一阶段也会对UI进行一些细微的美化,尽量去尝试实现更炫的效果,比如提供主题的切换,界面切换的动画等等。小巫需要继续去学习,然后把所有东西分享给学习Android的朋友们,感谢你们的关注,让我们一起共同学习进步。
第二阶段具体会什么时候启动,还没有明确的时间,因为最近开始忙起来了,所以尽情等待吧。哇咔咔



在播放界面点击除唱片外的任意一处区域可实现收藏按钮和模式选择按钮的显示,这俩个按钮是默认被隐藏的,再次点击界面上除唱片外的任意一处,这俩个按钮又会被隐藏起来。点击唱片会跳转到歌词显示的界面,如果在本机中有与歌词匹配的歌词文件,那么歌词会显示在此界面上,歌词以滚动的形式显示,而且会与歌曲的演唱匹配在歌词显示的界面有调节音量的拖动条,在歌词显示的界面点击红色的话筒按钮会退出此界面回到正在播放的界面,在正在播放的界面向右滑动会进入到信息显示的界面,在信息显示的界面点击歌手列表会显示出所有歌手的名字和头像,点击专辑列表显示出歌曲专辑的名称以及封面照片和发行时间,点击我的收藏会显示自己收藏的歌曲的名称,歌唱者以及歌曲的大小。此播放器的主要三个界面歌曲列表界面、正在播放界面、主要信息界面是以滑动的形式切换,在滑动切换界面后,每个界面的图标会相应的变色,当然也可以点击各界面的图标实现界面的切换。以上基本就是此播放器的所有功能了。 以上一大堆废话还是有些BUg的,而且做得也不太好,大家轻喷,马马虎虎看吧,做了3天,第一次上传,歌词文件一定要与歌曲文件同名,放在SD卡下,如果在genymotion中导入了歌曲和歌词,但运行程序不显示歌曲,那就需要重启genymotion了。总之欢迎下载吧。
评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小巫技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值