业务:android的程序,实现播放音乐的功能。
知识点:
1.使用了android的四大组件中的三个(Activity,Service,Broadcast)
activity与serivice之间的通信用广播Broadcast组件实现,程序中有10多个广播的发布和接收。
2.还使用了线程和Application类来保存全局性的数据。
在Service中启动一个线程是为了在activity需要的时候,不间断地给activity发布更新进度的广播,activity收到后再界面上展示出来。
难点:
个人感觉广播虽然多,但是逻辑上一般,不是很复杂,稍微比较难理解的是线程的启动和停止条件的判段和service中Destroy方法中的收尾清理工作。
1.在这几种条件下需要唤醒线程。
1)play()方法中,当开始播放音乐的时候,让它就开始干活。
2)seekto()方法中,当拖动seekBar需要重新定位并且播放音乐的时候,让它就开始干活。
3)当activity发布广播,请求service发布一个更新进度的广播的时候需要唤醒线程,让它就开始干活。
4)Service方法要退出销毁的时候需要唤醒线程,让其退出循环。
完整项目代码:
http://download.csdn.net/detail/tzguo1314/5542427
部分代码:
主Activity的全部代码
package com.tarena.day1901;
import com.tarena.entity.Music;
import com.tarena.utils.Consts;
import com.tarena.utils.FormatUtils;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class MainActivity extends Activity {
private ListView lvMusics;
private MenuInflater inflater;
private MusicAdapter adapter;
private Button btnPlayOrPause;
private MusicApplication app;
private TextView tvMusicName;
private TextView tvDuration;
private InnerBroadcastReceiver receiver;
private SeekBar sbProgress;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inflater=getMenuInflater();
app=(MusicApplication)getApplication();
//设置界面
setupView();
addlistener();
//启动service
Intent intent=new Intent(this, MusicService.class);
startService(intent);
//创建一个广播接收器
createReceiver();
}
//activity启动,发送广播给service告知需要发送进度更新的广播
@Override
protected void onStart() {
super.onStart();
Intent intent=new Intent(Consts.ACTION_NEED_UPDATE_PROGRESS);
intent.putExtra(Consts.EXTRA_NEED_UPDATE_PROGRESS, true);
sendBroadcast(intent);
Log.i("info", "onStart");
}
@Override
protected void onResume() {
Log.i("info", "onREsume");
super.onResume();
}
//activity停止,发送广播给service告知可以停止发送进度更新的广播
@Override
protected void onStop() {
super.onStop();
Intent intent=new Intent(Consts.ACTION_NEED_UPDATE_PROGRESS);
intent.putExtra(Consts.EXTRA_NEED_UPDATE_PROGRESS, false);
sendBroadcast(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);//MainActivity销毁的时候,对广播接收器,解除注册
}
//设置界面
public void setupView(){
lvMusics=(ListView)findViewById(R.id.lvMusics);
adapter=new MusicAdapter(this);
lvMusics.setAdapter(adapter);
btnPlayOrPause=(Button)findViewById(R.id.btnPlayOrPause);
tvMusicName=(TextView)findViewById(R.id.tvMusicName);
tvDuration=(TextView)findViewById(R.id.tvDuration);
sbProgress=(SeekBar)findViewById(R.id.sbProgress);
}
//添加菜单
@Override
public boolean onCreateOptionsMenu(Menu menu) {
inflater.inflate(R.menu.opts,menu);
return super.onCreateOptionsMenu(menu);
}
//选择系统菜单选项,发送广播
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent intent=null;
switch (item.getItemId()) {
case R.id.menu_sub_loop:
intent=new Intent(Consts.ACTION_PLAY_MODE);
intent.putExtra(Consts.EXTRA_PLAY_MODE, Consts.PLAY_MODE_LOOP);
sendBroadcast(intent);
Log.i("info", "循环");
break;
case R.id.menu_sub_random:
intent=new Intent(Consts.ACTION_PLAY_MODE);
intent.putExtra(Consts.EXTRA_PLAY_MODE, Consts.PLAY_MODE_RADOM);
sendBroadcast(intent);
Log.i("info", "随机");
break;
case R.id.menu_opts_exit:
intent=new Intent(Consts.ACTION_APP_STOPED);
sendBroadcast(intent);
finish();
break;
}
return super.onOptionsItemSelected(item);
}
//监听用户拖动seekbar,并记录拖动的位置
private void addlistener() {
sbProgress.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(fromUser){
Intent intent=new Intent(Consts.ACTION_SEEK_TO);
intent.putExtra(Consts.EXTRA_SEEK_TO, progress);
sendBroadcast(intent);
}
}
});
}
//用户不同的操作,执行相应的操作
public void doClick(View v){
Intent intent=null;
switch (v.getId()) {
case R.id.btnNext:
intent=new Intent(Consts.ACTION_PLAY_NEXT);
btnPlayOrPause.setText("暂停");
break;
case R.id.btnPlayOrPause:
String text=((Button)v).getText().toString();
if("播放".equals(text)){
intent=new Intent(Consts.ACTION_PLAY);
((Button)v).setText("暂停");
}else if("暂停".equals(text)){
intent=new Intent(Consts.ACTION_PAUSE);
((Button)v).setText("播放");
}
break;
case R.id.btnPrevious:
intent=new Intent(Consts.ACTION_PLAY_PREVIOUS);
btnPlayOrPause.setText("暂停");
break;
}
sendBroadcast(intent);
}
//创建一个广播接收器
private void createReceiver() {
receiver=new InnerBroadcastReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction(Consts.ACTION_CURRENT_MUSIC_CHANGE);
filter.addAction(Consts.ACTION_START_UPDATE_PROGRESS);
filter.addAction(Consts.ACTION_SEND_PLAYING_STATE);
registerReceiver(receiver, filter);
}
private class InnerBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action=intent.getAction();
//接收广播,音乐变更,前端展示更新为新的音乐的名字和时长
if(Consts.ACTION_CURRENT_MUSIC_CHANGE.equals(action)){
Music music=app.getCurrentMusic();
tvMusicName.setText(music.getName());
tvDuration.setText(FormatUtils.format(music.getDuration()));
}else if(Consts.ACTION_START_UPDATE_PROGRESS.equals(action)){
//当前播放时长
int progress=intent.getIntExtra(Consts.EXTRA_CURRENT_MUSIC_TIME_POSITION, 0);
//总时长
int Duration = (int)app.getCurrentMusic().getDuration();
tvDuration.setText(FormatUtils.format(progress));//在界面显示歌曲已播放时长
sbProgress.setProgress(progress*100/Duration);
}else if(Consts.ACTION_SEND_PLAYING_STATE.equals(action)){
int state=intent.getIntExtra(Consts.EXTRA_PLAY_STATE, Consts.STATE_OTHERS);
Music m=app.getCurrentMusic();
int progress=intent.getIntExtra(Consts.EXTRA_CURRENT_MUSIC_TIME_POSITION, 0);
int Duration = (int)app.getCurrentMusic().getDuration();
tvDuration.setText(FormatUtils.format(progress));//在界面显示歌曲已播放时长
sbProgress.setProgress(progress*100/Duration);
tvMusicName.setText(m.getName());
if(state==Consts.STATE_PAUSE){
btnPlayOrPause.setText("播放");
}else if(state==Consts.STATE_ISPLAYING){
btnPlayOrPause.setText("暂停");
}
}
}
}
}
package com.tarena.day1901;
import java.io.IOException;
import java.util.Random;
import com.tarena.entity.Music;
import com.tarena.utils.Consts;
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.os.IBinder;
import android.util.Log;
public class MusicService extends Service {
private MediaPlayer player;
private MusicApplication app;
private boolean isPause;
private boolean needUpdate; //需要发布更新进度
private boolean isLoop;//控制循环语句
private int playMode;
private Random random;
private InnerBroadcastReceiver receiver;
private Thread workThread;
@Override
public void onCreate() {
super.onCreate();
app=(MusicApplication)getApplication();
player=new MediaPlayer();
playMode=Consts.PLAY_MODE_LOOP;
isLoop=true;
needUpdate=true;
random=new Random();
//设置自动播放下一首的监听器
addlistener();
//添加接收器
createReceiver();
workThread=new Thread(){
public void run() {
while(isLoop){
while(isLoop&&needUpdate&&player.isPlaying()){
Intent intent=new Intent(Consts.ACTION_START_UPDATE_PROGRESS);
//将当前播放的音乐播放的时间抽的位置包装传递
intent.putExtra(Consts.EXTRA_CURRENT_MUSIC_TIME_POSITION, player.getCurrentPosition());
sendBroadcast(intent);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//此工作线程需要不停地循环工作,除非修改循环标志。不需要发送广播的时候就等待,睡大觉
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
};
workThread.start();
}
//service销毁的时候,解除注册广播接收器
@Override
public void onDestroy() {
unregisterReceiver(receiver);
//唤醒线程,让线程退出循环
isLoop = false;
synchronized (workThread) {
workThread.notify();
}
//如果音乐还在播放,停止其播放
if (player.isPlaying())
player.stop();
//释放资源
player.release();
super.onDestroy();
}
//如果是isPause状态值为true,直接播放,else重置player,最后要记得唤醒线程
public void play(){
if(isPause){
player.start();
}else{
Music music=app.getCurrentMusic();
String path=music.getMusicPath();
if(path!=null){
try {
player.reset();
player.setDataSource(path);
player.prepare();
player.start();
//当前索引所表示的歌曲变化,发布广播
Intent intent=new Intent(Consts.ACTION_CURRENT_MUSIC_CHANGE);
sendBroadcast(intent);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
isPause=false;//音乐开始播放,将暂停状态标志置为false
synchronized (workThread) {
workThread.notify();
}
}
//如果正在播放,暂停播放
public void pause(){
if(player.isPlaying()){
player.pause();
isPause=true;
}
}
//开始播放后一首
public void next(){
int index=app.getCurrentPosition();
switch (playMode) {
case Consts.PLAY_MODE_LOOP:
if(++index>=app.getPlayListSize()){
index=0;
}
break;
case Consts.PLAY_MODE_RADOM:
do {
index=random.nextInt(app.getPlayListSize());
} while (index==app.getCurrentPosition());
break;
}
app.setCurrentPosition(index);
isPause=false;
play();
}
//开始播放前一首
public void previous(){
int index=app.getCurrentPosition();
switch (playMode) {
case Consts.PLAY_MODE_LOOP:
if(--index<=0){
index=app.getPlayListSize()-1;
}
break;
case Consts.PLAY_MODE_RADOM:
do {
index=random.nextInt(app.getPlayListSize());
} while (index==app.getCurrentPosition());
break;
}
app.setCurrentPosition(index);
isPause=false;
play();
}
//定位到指定的时间值开始播放
public void seekTo(int positon){
player.seekTo(positon);
player.start();
synchronized (workThread) {
workThread.notify();
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
//设置自动播放下一首
public void addlistener(){
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
next();
}
});
}
//添加接收器
private void createReceiver() {
receiver=new InnerBroadcastReceiver();
IntentFilter filter=new IntentFilter();
filter.addAction(Consts.ACTION_PAUSE);
filter.addAction(Consts.ACTION_PLAY);
filter.addAction(Consts.ACTION_PLAY_NEXT);
filter.addAction(Consts.ACTION_PLAY_PREVIOUS);
filter.addAction(Consts.ACTION_PLAY_MODE);
filter.addAction(Consts.ACTION_APP_STOPED);
filter.addAction(Consts.ACTION_NEED_UPDATE_PROGRESS);
filter.addAction(Consts.ACTION_SEEK_TO);
registerReceiver(receiver, filter);
}
//接收器类
private class InnerBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action=intent.getAction();
if(Consts.ACTION_PLAY.equals(action)){
play();
}else if(Consts.ACTION_PAUSE.equals(action)){
pause();
}else if(Consts.ACTION_PLAY_NEXT.equals(action)){
next();
}else if(Consts.ACTION_PLAY_PREVIOUS.equals(action)){
previous();
}else if(Consts.ACTION_PLAY_MODE.equals(action)){
playMode=intent.getIntExtra(Consts.EXTRA_PLAY_MODE, Consts.PLAY_MODE_LOOP);
}
//获取并判断当前是否需要发送更新进度的广播
else if(Consts.ACTION_NEED_UPDATE_PROGRESS.equals(action)){
needUpdate=intent.getBooleanExtra(Consts.EXTRA_NEED_UPDATE_PROGRESS, true);
if(needUpdate){
synchronized (workThread) {
workThread.notify();
}
//更新进度的时候,同时发送音乐播放器的状态(是否正在播放、播放的音乐的位置)
Intent intent2=new Intent(Consts.ACTION_SEND_PLAYING_STATE);
int state=Consts.STATE_OTHERS;
int position=0;
if(player.isPlaying()){
state=Consts.STATE_ISPLAYING;
}else if(isPause){
state=Consts.STATE_PAUSE;
}
position=player.getCurrentPosition();
intent2.putExtra(Consts.EXTRA_CURRENT_MUSIC_TIME_POSITION, position);
intent2.putExtra(Consts.EXTRA_PLAY_STATE, state);
sendBroadcast(intent2);
}
//根据拖动的位置,计算应当开始播放的时间值
}else if(Consts.ACTION_SEEK_TO.equals(action)){
int progress=intent.getIntExtra(Consts.EXTRA_SEEK_TO, player.getCurrentPosition());
int duration=(int)app.getCurrentMusic().getDuration();
seekTo(progress*duration/100);
}else if(Consts.ACTION_APP_STOPED.equals(action)){
stopSelf();
}
}
}
}