接着 音乐播放器2.0(上) 对 音乐播放器1.0做改造。把音乐播放改为使用 Service,实现可以后台播放音乐。
开发步骤是按照 【达内课程】Service 中总结的开发步骤做的。
1、新建 PlayMusicService
新建 PlayMusicService 继承 Service,放到 Service 文件夹下。
public class PlayMusicService extends Service {
public PlayMusicService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
检查下 AndroidManifest.xml 中是否注册了
<service
android:name=".Service.PlayMusicService"
android:enabled="true"
android:exported="true"/>
2、创建 IMusicPlayer 接口,定义要实现的方法
创建 IMusicPlayer 接口,并在接口中定义由 Service 实现功能,但由 Activity 调用的方法。例如点击一个播放按钮,当然是由 Activity 调用的,但是为了保证优先级,这些方法应该在 Service 中定义。
public interface IMusicPlayer<T> {
//播放歌曲
void play();
/**
* 播放指定position对应的歌曲
*
* @param position
*/
void play(int position);
//暂停歌曲
void pause();
//下一首歌曲
void next();
//上一首歌曲
void previous();
/**
* 获取当前播放歌曲的总时长
*
* @return 当前播放歌曲的总时长
*/
int getDuration();
/**
* 获取当前播放到的位置
*
* @return 当前播放到的位置 单位:毫秒
*/
int getCurrentPosition();
/**
* 获取当前正在播放的歌曲的索引
*/
int getCurrentMusicPosition();
/**
* 获取当前播放歌曲的播放状态
*
* @return 当前播放歌曲的播放状态,true:正在播放;false:停止播放
*/
boolean isPlaying();
}
这些方法都是我们在 MainActivity 中使用的。如果一次性写不完可以用到再添加。把这个文件放在Util(Utility工具文件夹)或Core(核心文件夹)中。
3、在 PlayMusicService 中创建内部类 IBinder类,为和 Activity 连接做准备
在 PlayMusicService 中创建内部类 IBinder 类,继承自 Binder,实现 IMusicPlayer接口,并实现接口中的方法。
然后 PlayMusicService 的onBind()
方法中创建 InnerBiner 对象,并作为该方法的返回值。
public class PlayMusicService extends Service {
public PlayMusicService() {
}
@Override
public IBinder onBind(Intent intent) {
InnerBinder binder = new InnerBinder();
return binder;
}
private class InnerBinder extends Binder implements IMusicPlayer {
@Override
public void play() {
Log.d("Music", "由Service调用play()");
}
@Override
public void play(int position) {
Log.d("Music", "由Service调用play(int position)");
}
@Override
public void pause() {
}
@Override
public void previous() {
}
@Override
public void next() {
}
@Override
public int getCurrentMusicPosition() {
return 0;
}
@Override
public int getCurrentPosition() {
return 0;
}
@Override
public int getDuration() {
return 0;
}
@Override
public boolean isPlaying() {
return false;
}
}
}
4、MainActivity 中绑定 Service
MainActivity 中创建内部类 InnerServiceConnection,实现 ServiceConnection 接口。声明 IMusicPlayer 类型的全局变量 player,并在 InnerServiceConnection 中 onServiceConnected()
方法中通过第二个参数直接赋值,并强制转换类型。
/**
* 与Service实现绑定时建立的连接对象
* 声明成内部类或接口类型都可以
*/
private ServiceConnection conn;
/**
* 调用Service中实现的功能的对象
*/
private IMusicPlayer player;
private class InnerServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
player = (IMusicPlayer)iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
onCreate()
方法中增加绑定 Service 的方法:bindService()
//绑定Service
Intent intent = new Intent(this, PlayMusicService.class);
conn = new InnerServiceConnection();
bindService(intent,conn,BIND_AUTO_CREATE);
5、MainActivity 中调用 PlayMusicService 中的方法
MainActivity 中需要调用 PlayMusicService 中的方法时,直接用 player 调用即可。所以可以把 onClick 中代码改为:
//按钮监听
private class InnerClassOnClickListener implements View.OnClickListener {
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.ib_play:
if (player.isPlaying()) {
player.pause();
} else {
player.play();
}
break;
case R.id.ib_previous:
player.previous();
break;
case R.id.ib_next:
player.next();
break;
}
}
}
6、MainActivity 中的 onDestroy 中解除与 Service 的绑定
@Override
protected void onDestroy() {
//解除与Service的绑定
unbindService(conn);
//停止更新进度的线程
stopThread();
//释放资源
mediaPlayer.release();
mediaPlayer = null;
super.onDestroy();
}
现在运行程序,点击播放按钮,输出日志如下
7、PlayMusicService 中完善播放器的方法
PlayMusicService 中完善播放器的方法,把 MainActivity 中的相应方法、相关变量拿过来,稍作修改。有些改变页面上图标的地方,例如 play()
的时候会把暂停按钮改成播放按钮,这个操作可以放在 Activity 的 onClick() 事件中,这样写比较方便。
内部类 InnerBinder 直接调用上面写的方法,同时在 onDestroy
方法执行时释放相关资源。
相应的 MainActivity 中的相应方法、相关变量都可以删除了。调用方法的地方可以使用刚刚声明的 IMusicPlayer 类型的全局变量 player 直接调用。
8、在 MainActivity 中开启一个线程,查询 service 中状态
显示歌曲的时候 Activity 是主动方,Service 没有办法主动告诉 Activity 自己目前播放的是哪首歌,因此应该在 MainActivity 中开启一个线程,每隔 1 秒查询 Service 中状态。同时增加开启线程,关闭线程的方法。可以对之前的 UpdateProgressThread 进行修改。
把格式化时间的方法单独放到一个 CommonUtil 的工具类中方便使用。
在点击播放/暂停,上一首,下一首等按钮时,开启或关闭相应线程即可,同时在onDestroy()
方法中停止线程。
这部分代码先不放了,有点繁琐,下一章直接上完整代码。