我们今天的目的是学习如何创建一个简单的音乐播放器,可支持播放,暂停,继续播放,以及进度显示,已经进度拖拽。
既然我们的目的已经很明确,那我们先分析一下:
1: 我们播放音乐的任务可以放在Activity中吗?
假设我们当前音乐播放,我们切到别的应用,这时候播放音乐的Activity所在的进程就属于后台进程,因为后台进程会由于系统资源不足时,会杀死当前进程,会导致音乐不播放了。所以采用Activity是不行的。
2:播放音乐的任务放在一个服务中,那服务有2中启动方式,应该使用那种服务?
a:如果采用StartServer启动服务,那服务里的播放音乐的方法是没法拿到的。
b:如果采用BindService启动服务,但是BindService启动的服务,当Activity退出时,服务也会退出的。
3:所以,我们就将2中服务结合起来,用StartServer启动服务,然后用BindService绑定服务,这时候即便是Activity退出了,服务也照常工作。同时也可以拿到服务里的音乐控制方法。
那既然分析清楚了如何做。那我们就开始行动。
第一步: 先创建一个简单的Activity,里面有播放,暂停,继续播放,退出按钮(布局文件就不贴出了)。然后启动服务
代码语言:javascript复制 //启动服务,然后需要绑定服务
conn = new MyServiceConn();
intent = new Intent(this, MusicService.class);
startService(intent);
bindService(intent, conn, BIND_AUTO_CREATE);
第二步:在服务的OnCreate方法中创建MediaPlayer
代码语言:javascript复制 @Override
public void onCreate() {
// 服务创建时,我们创建MediaPlayer
super.onCreate();
player = new MediaPlayer();
}
第三步:启动音乐播放,启动音乐播放当然要有步骤的:
我们先看一张关于如何启动音乐的图
从上图中可以看到,启动音乐播放需要几个步骤:
1:reset(重置)
2:setDataSource(设置数据源)
3:prepare(准备)
4:start(播放)
如果当前正在播放,需要暂停就需要调用pause()
如果想从暂停回到播放就需要调用(onstart)
如果向停止播放就需要调用(onstop),如果向接着播放就需要重新prepare,start
一下是如何启动音乐播放:
代码语言:javascript复制//服务的播放方法
public void play()
{
//1: 先重置
player.reset();
try {
//2:设置数据
player.setDataSource("sdcard/smallapple.mp3");
//3.准备
player.prepare();
//4.开始播放
player.start();
//5.启动定时器
addTimer();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
暂停方法:
代码语言:javascript复制 //服务的暂停服务
public void pause()
{
player.pause();
}
继续播放:
代码语言:javascript复制 //服务的继续播放
public void continuePlay()
{
player.start();
}
第四步: 如果向给音乐播放增加进度显示,那我们添加一个SeekBar,这样 以后还可以拖拽
既然需要每秒都使得进度条的进度改变,那就需要增加一个定时器,定时器每秒得到当前播放进度,然后通知UI显示更新
代码语言:javascript复制//添加定时器,用于每秒更新播放进度
public void addTimer()
{
if(timer == null)
{
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//获取音乐的总时间
int duration = player.getDuration();
//获取音乐的当前播放时间
int currposition = player.getCurrentPosition();
//获得到的数据,需要传入到UI更新,我们用msg传送
Message msg = MainActivity.handler.obtainMessage();
//通过bundle传送数据
Bundle data = new Bundle();
data.putInt("duration", duration);
data.putInt("currposition", currposition);
msg.setData(data);
//发送消息给Activity
MainActivity.handler.sendMessage(msg);
}
//开始计时任务后的5毫秒,第一次执行run方法,以后每1000毫秒执行一次
}, 5, 1000);
}
}
这时候当播放后,定时器获得到数据就会传送给MainActivity用于显示:
代码语言:javascript复制 static Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg) {
Bundle data = msg.getData();
sBar.setMax(data.getInt("duration"));
sBar.setProgress(data.getInt("currposition"));
};
};
第五步: 如果我们想实现拖拽,就可以给SeekBar增加Seekbar改变监听
代码语言:javascript复制sBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
mInterface.seekTo(seekBar.getProgress());
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
// TODO Auto-generated method stub
}
});
因为当我们手指滑动的过程中我们不需要让播放进度,只有当手指抬起后才播放,那我们只需要在onStopTrackingTouch中更新播放进度
到这里大概就完事了,以下是详细 代码
MainActivity中代码:
代码语言:javascript复制public class MainActivity extends Activity {
MusicInterface mInterface;
private static SeekBar sBar;
private MyServiceConn conn;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sBar = (SeekBar) findViewById(R.id.seekBar1);
sBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
mInterface.seekTo(seekBar.getProgress());
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
// TODO Auto-generated method stub
}
});
//启动服务,然后需要绑定服务
conn = new MyServiceConn();
intent = new Intent(this, MusicService.class);
startService(intent);
bindService(intent, conn, BIND_AUTO_CREATE);
}
static Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg) {
Bundle data = msg.getData();
sBar.setMax(data.getInt("duration"));
sBar.setProgress(data.getInt("currposition"));
};
};
class MyServiceConn implements ServiceConnection
{
@Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
//获得服务中间人
mInterface = (MusicInterface)arg1;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
// TODO Auto-generated method stub
}
}
//开始播放
public void start(View v)
{
mInterface.play();
}
//暂停播放
public void pause(View v)
{
mInterface.pause();
}
//继续播放
public void continuePlay(View v)
{
mInterface.continuePlay();
}
//退出
public void exit(View v)
{
//先解绑服务
unbindService(conn);
//在停止服务
stopService(intent);
finish();
}
}
MusicService中代码:
代码语言:javascript复制public class MusicService extends Service {
MediaPlayer player;
private Timer timer;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return new MusicController();
}
@Override
public void onCreate() {
// 服务创建时,我们创建MediaPlayer
super.onCreate();
player = new MediaPlayer();
}
@Override
public void onDestroy() {
// 销毁服务时,需要先停止播放,再释放资源
super.onDestroy();
//停止服务
player.stop();
//释放底层硬件占有的资源
player.release();
player = null;
if(timer != null)
{
//取消定时器
timer.cancel();
timer = null;
}
}
class MusicController extends Binder implements MusicInterface
{
@Override
public void play() {
// TODO Auto-generated method stub
MusicService.this.play();
}
@Override
public void pause() {
// TODO Auto-generated method stub
MusicService.this.pause();
}
@Override
public void continuePlay() {
// TODO Auto-generated method stub
MusicService.this.continuePlay();
}
@Override
public void seekTo(int progress) {
// TODO Auto-generated method stub
MusicService.this.seekTo(progress);
}
}
//服务的播放方法
public void play()
{
//1: 先重置
player.reset();
try {
//2:设置数据
player.setDataSource("sdcard/smallapple.mp3");
//3.准备
player.prepare();
//4.开始播放
player.start();
//5.启动定时器
addTimer();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//服务的暂停服务
public void pause()
{
player.pause();
}
//服务的继续播放
public void continuePlay()
{
player.start();
}
//服务的拖动移动
public void seekTo(int progress)
{
player.seekTo(progress);
}
//添加定时器,用于每秒更新播放进度
public void addTimer()
{
if(timer == null)
{
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//获取音乐的总时间
int duration = player.getDuration();
//获取音乐的当前播放时间
int currposition = player.getCurrentPosition();
//获得到的数据,需要传入到UI更新,我们用msg传送
Message msg = MainActivity.handler.obtainMessage();
//通过bundle传送数据
Bundle data = new Bundle();
data.putInt("duration", duration);
data.putInt("currposition", currposition);
msg.setData(data);
//发送消息给Activity
MainActivity.handler.sendMessage(msg);
}
//开始计时任务后的5毫秒,第一次执行run方法,以后每1000毫秒执行一次
}, 5, 1000);
}
}
}
MusicInterface中代码:
代码语言:javascript复制public interface MusicInterface {
void play();
void pause();
void continuePlay();
void seekTo(int progress);
}
显示效果如下: