Android_播放器_利用Service通过MediaPlayer播放歌曲并完成歌词同步绘制

本博文为子墨原创,转载请注明出处!
http://blog.csdn.net/zimo2013/article/details/16849695

1.示意图

Android_播放器_利用Service通过MediaPlayer播放歌曲并完成歌词同步绘制_第1张图片           Android_播放器_利用Service通过MediaPlayer播放歌曲并完成歌词同步绘制_第2张图片

2. 代码实现

/**
 * MainActivity.java
 * @author zimo2013
 * @see http://blog.csdn.net/zimo2013
 *
 */
public class MainActivity extends Activity implements OnClickListener,
		OnSeekBarChangeListener {

	private LrcSurfaceView surfaceView;
	private Button btPlay;
	private Button btPause;
	private Button btStop;
	private SeekBar bar;

	private PlayerConn conn;
	private IMyBinder binder;

	private boolean isDrag;	//进度条是否正在拖拽
	private List<LrcInfo> list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initViews();
		
		list = new ArrayList<LrcInfo>();
		conn = new PlayerConn();
		Intent intent = new Intent(this, PlayService.class);
		bindService(intent, conn, BIND_AUTO_CREATE);

		bar.setOnSeekBarChangeListener(this);
		btPlay.setOnClickListener(this);
		btPause.setOnClickListener(this);
		btStop.setOnClickListener(this);

		parseLrc();
	}

	private void initViews() {
		surfaceView = (LrcSurfaceView) findViewById(R.id.lrc);
		btPlay = (Button) findViewById(R.id.bt_play);
		btPause = (Button) findViewById(R.id.bt_pause);
		btStop = (Button) findViewById(R.id.bt_stop);
		bar = (SeekBar) findViewById(R.id.bar);
	}

	/**
	 * 歌词解析
	 */
	public void parseLrc() {
		BufferedReader bufr = null;
		try {
			if (Environment.MEDIA_MOUNTED.equals(Environment
					.getExternalStorageState())) {
				bufr = new BufferedReader(new InputStreamReader(new FileInputStream(
						Environment.getExternalStorageDirectory().getAbsolutePath()+ "/music.lrc")));
				
				String data = null;
				//添加第一条以歌名开头的歌词
				list.add(new LrcInfo(0, "music"));
				// "[00:09.77]Why did the writer complain to the people behind him?"
				Pattern pattern = Pattern.compile("\\[(\\d{2}):(\\d{2}.\\d{2,3})\\](.*)");
				while ((data = bufr.readLine()) != null) {
					Matcher matcher = pattern.matcher(data);
					while (matcher.find()) {
						int minute = Integer.parseInt(matcher.group(1));//分钟 00 
						float second = Float.parseFloat(matcher.group(2));//秒 09.77
						list.add(new LrcInfo((int) ((minute * 60 + second) * 1000), matcher.group(3)));
					}
				}
				//为surfaceView设定歌词
				surfaceView.setList(list);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if(bufr != null){
				try {
					bufr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@Override
	public void onClick(View v) {
		Intent intent = new Intent(this, PlayService.class);
		if (btPlay == v) {
			intent.setAction(PlayService.ACTION_PLAY);
		} else if (btPause == v) {
			intent.setAction(PlayService.ACTION_PAUSE);
		} else if (btStop == v) {
			intent.setAction(PlayService.ACTION_STOP);
		}
		startService(intent);
	}

	private class PlayerConn implements ServiceConnection {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			binder = (IMyBinder) service;
			binder.setHandler(handler);	//设定handler
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {

		}
	}

	private Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			System.out.println("msg what:" + msg.what + " arg1:" + msg.arg1);
			switch (msg.what) {
			case PlayService.WHAT_DURATION:
				bar.setMax(msg.arg1);
				break;
			case PlayService.WHAT_CURRENT:
				if (!isDrag) {
					//更新进度条以及歌词
					bar.setProgress(msg.arg1);
					surfaceView.update(msg.arg1);
				}
				break;
			}
		}
	};

	@Override
	public void onProgressChanged(SeekBar seekBar, int progress,
			boolean fromUser) {
		if(fromUser){
			surfaceView.update(bar.getProgress());//更新歌词
		}
	}

	@Override
	public void onStartTrackingTouch(SeekBar seekBar) {
		//移除所有未处理的消息
		handler.removeMessages(PlayService.WHAT_CURRENT);
		isDrag = true;
	}

	@Override
	public void onStopTrackingTouch(SeekBar seekBar) {
		//拖拽完成,更新
		Intent intent = new Intent(this, PlayService.class);
		intent.setAction(PlayService.ACTION_PLAY);
		intent.putExtra("progress", bar.getProgress());
		startService(intent);
		isDrag = false;
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		//解除服务
		unbindService(conn);
	}
}
/**
 * PlayService.java
 * @author zimo2013
 * @see http://blog.csdn.net/zimo2013
 *
 */
public class PlayService extends Service {
	
	public static final String ACTION_PLAY = "play";
	public static final String ACTION_PAUSE = "pause";
	public static final String ACTION_STOP = "stop";

	public static final int WHAT_CURRENT = 1;
	public static final int WHAT_DURATION = 2;

	private MediaPlayer player;
	private Handler handler;
	private IMyBinder binder;
	private int progress;

	@Override
	public void onCreate() {
		super.onCreate();
		binder = new MyBinder();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return (IBinder) binder;
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		String action = intent.getAction();
		if (ACTION_PLAY.equals(action)) {
			progress = intent.getIntExtra("progress", 0);
			toPlay(progress);
		} else if (ACTION_PAUSE.equals(action)) {
			pause();
		} else if (ACTION_STOP.equals(action)) {
			stop();
		}
		return super.onStartCommand(intent, flags, startId);
	}
	@Override
	public void onDestroy() {
		super.onDestroy();
		if(player != null){
			player.release();	//是否资源
			player = null;		//置空,拷贝GC回收
		}
	}
	/**
	 * 将要播放
	 * @param pos
	 */
	private void toPlay(int pos) {
		checkMediaPlayer();
		if (state == PlayerState.Initialized) {
			state = PlayerState.Preparing;
			player.prepareAsync();
		} else {
			play(pos);
		}
	}

	/**
	 * 实际播放音乐
	 * @param pos
	 */
	private void play(int pos) {
		if (state == PlayerState.Started || state == PlayerState.Prepared
				|| state == PlayerState.Paused
				|| state == PlayerState.PlaybackCompleted) {
			
			if (state != PlayerState.Paused) {
				player.seekTo(pos);
			}
			player.start();
			state = PlayerState.Started;
			new Thread() {
				public void run() {
					// updateUI();
					while (state == PlayerState.Started) {
						send(WHAT_CURRENT, player.getCurrentPosition());
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}.start();
		} else {
			show("播放异常");
		}
	}
	/**
	 * 暂停播放
	 */
	private void pause() {
		if (state == PlayerState.Started) {
			player.pause();
			state = PlayerState.Paused;
		} else if (state == PlayerState.Paused) {
			show("播放已经暂停,现不需要再暂停!");
		} else {
			show("暂停异常");
		}
	}
	/**
	 * 停止播放
	 */
	private void stop() {
		if (state == PlayerState.Started || state == PlayerState.Prepared
				|| state == PlayerState.Paused
				|| state == PlayerState.PlaybackCompleted) {
			player.release();	//是否资源
			player = null;		//置空,拷贝GC回收
			state = PlayerState.End;
		} else {
			show("停止异常");
		}
	}

	/**
	 * 发送消息,用于更新ui
	 * @param what
	 * @param value
	 */
	private void send(int what, int value) {
		// 更新进度条
		Message msg = handler.obtainMessage();
		msg.what = what;
		msg.arg1 = value;
		handler.sendMessage(msg);
	}

	/**
	 * 检查MediaPlayer对象,如果不存在则应该创建,并完成相关初始化
	 */
	private void checkMediaPlayer() {
		if (player == null) {
			// 完成多媒体的初始化
			player = new MediaPlayer();
			state = PlayerState.Idle;
			player.setAudioStreamType(AudioManager.STREAM_MUSIC);
			
			// 判断SDcard正常挂载
			if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
				try {
					player.setDataSource(Environment
							.getExternalStorageDirectory().getAbsolutePath()
							+ "/music.mp3");
					state = PlayerState.Initialized;
					//异步准备
					player.setOnPreparedListener(new OnPreparedListener() {

						@Override
						public void onPrepared(MediaPlayer mp) {
							state = PlayerState.Prepared;
							// 设定进度条的max值
							send(WHAT_DURATION, player.getDuration());
							// 准备完成
							play(progress);
						}
					});
					//播放完成
					player.setOnCompletionListener(new OnCompletionListener() {

						@Override
						public void onCompletion(MediaPlayer mp) {
							state = PlayerState.PlaybackCompleted;
							// 播放完成
							play(0);
							show("已经播放完成,现在正在重新播放");
						}
					});

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 自定义MyBinder
	 * @author Administrator
	 *
	 */
	private class MyBinder extends Binder implements IMyBinder {

		@Override
		public void setHandler(Handler handler) {
			PlayService.this.handler = handler;
		}

	}

	/**
	 * 弹出吐司
	 * @param info
	 */
	public void show(String info) {
		Toast.makeText(getApplicationContext(), info, 0).show();
	}

	private PlayerState state = PlayerState.Idle;

	/**
	 * 播放器的播放状态枚举
	 * @author Administrator
	 *
	 */
	private enum PlayerState {
		Idle, Initialized, Preparing, Prepared, Started, Stopped, Paused, End, PlaybackCompleted,
	}
}
/**
 * LrcSurfaceView.java
 * @author zimo2013
 * @see http://blog.csdn.net/zimo2013
 *
 */
public class LrcSurfaceView extends SurfaceView {
	private SurfaceHolder holder;
	private List<LrcInfo> list;

	private boolean isCreated;
	private int mWidth;
	private int mHeight;
	private int pos = 0;
	private Paint paintNew;
	private Paint paintOld;
	private int current;

	private ExecutorService thread;
	private Runnable runnable;

	public LrcSurfaceView(Context context) {
		this(context, null, 0);
	}

	public LrcSurfaceView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
		this.getHolder();
	}

	public LrcSurfaceView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		holder = getHolder();
		holder.addCallback(new LrcCallBack());

		// 线程池
		thread = Executors.newSingleThreadExecutor();
		runnable = new LrcUpdateRunnable();

		// 创建2种画笔对象
		paintNew = new Paint();
		paintNew.setTextSize(20);
		paintNew.setTextAlign(Paint.Align.CENTER);
		paintNew.setColor(Color.YELLOW);

		paintOld = new Paint();
		paintOld.setTextSize(15);
		paintOld.setTextAlign(Paint.Align.CENTER);//居中显示
		paintOld.setColor(Color.WHITE);
	}

	/**
	 * 为surfaceView 设定歌词
	 * 
	 * @param list
	 */
	public void setList(List<LrcInfo> list) {
		Collections.sort(list); // 歌词排序,从前往后
		this.list = list;
	}

	/**
	 * 更新歌词信息
	 * 
	 * @param current
	 */
	public void update(int current) {
		if (isCreated && list != null && list.size() > 0) {// 如果surfaceView对应已经被创建或者存在歌词
			this.current = current;
			thread.execute(runnable); // 线程池更新歌词信息
		}
	}

	private class LrcUpdateRunnable implements Runnable {
		@Override
		public void run() {
			pos = 0;// 默认从第一条歌词开始寻找
			Canvas canvas = holder.lockCanvas();
			canvas.drawColor(Color.BLACK);
			while (true) {
				if (pos == list.size() - 1) {
					break;
				}
				if (current >= list.get(pos).startTime && current < list.get(pos + 1).startTime) {
					break;
				}
				pos++;
			}//Surface可以直接操作UI

			if (pos != 0) {// 上一条已经播放歌词
				canvas.drawText(list.get(pos - 1).lrc, mWidth / 2, mHeight / 2 - 50, paintOld);
			}
			// 当前正在播放歌词
			canvas.drawText(list.get(pos).lrc, mWidth / 2, mHeight / 2, paintNew);

			// 下一条需要播放歌词
			if (pos != list.size() - 1) {
				canvas.drawText(list.get(pos + 1).lrc, mWidth / 2, mHeight / 2 + 50, paintOld);
			}
			// 接触锁定并提交
			holder.unlockCanvasAndPost(canvas);
		}
	}

	private class LrcCallBack implements SurfaceHolder.Callback {

		@Override
		public void surfaceCreated(SurfaceHolder holder) {
		}

		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
			isCreated = true;
			mHeight = height;
			mWidth = width;
		}

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
		}
	}

}
/**
 * IMyBinder.java
 * @author zimo2013
 * @see http://blog.csdn.net/zimo2013
 *
 */
public interface IMyBinder {

	void setHandler(Handler handler);
}

你可能感兴趣的:(android,service,音乐播放器,多媒体,mediaplayer)