第4节 播放服务的搭建
播放音乐的服务-MusicService
是整改音乐播放器的核心,它将播放界面和实际的播放功能连接在一起。

4.1 MusicService的接口
它对外要提供两类主要的功能,播放音乐的控制接口和播放列表的存取接口。
4.1.1 播放音乐的控制接口
根据功能的定义,我们认为控制接口应该包括:
play():播放播放列表中应该要播放的音乐;
playPre():播放播放列表中的上一首音乐;
playNext():播放播放列表中的下一首音乐;
pause():暂停播放;
isPlaying():当前音乐是否处于播放的状态;
seekTo():将当前音乐播放的进度,拖动到指定的位置;
getCurrentMusicItem():获取当前正在播放的音乐的信息;
MusicListActivity
为了获取MusicService
的状态,还需要设置监听器,当MusicService
有变化时,能主动通知到MusicListActivity
。所以要有监听函数:
registerOnStateChangeListener():注册监听函数
unregisterOnStateChangeListener():注销监听函数
监听时要获取的信息包括:
- 播放进度的改变;
- 音乐开始播放;
- 音乐停止播放;
所以设计监听器的接口为,
public interface OnStateChangeListenr {
//用来通知播放进度
void onPlayProgressChange(MusicItem item);
//用来通知当前处于播放状态
void onPlay(MusicItem item);
//用来通知当前处于暂停或停止状态
void onPause(MusicItem item);
}
当然,还可以设计获取更多的信息,这里我们就简单的获取这几种简单的信息。只要知道了设计的原理,我们就可以在以后随心所欲的改造。
4.1.2. 播放列表的存取接口;
MusicService
通过操作PlayListContentProvider
来实现对音乐列表的存取。MusicService
对外提供这样的接口:
getPlayList():获取播放列表
addPlayList():添加播放列表。这里添加列表应该有两种形式,一种是一次性添加多首音乐,一种是一次就添加一首音乐。
4.2 Service的使用
安卓系统的Service
按照创建的方式进行分类,有两种:启动Service-start Service
,绑定Service-bind Service
。前者使用startService()
运行,后者使用bindService()
运行。
4.2.1 Start Service
其他组件通过调用startService()
函数将Service
运行起来,再通过调用stopService()
函数让其停止运行。
单纯的使用这种形式的Service
最为简单,它和它的调用者之间没有什么联系,调用者只是负责启动它和停止它,或者在启动它的时候通过Intent传递一点数据,除此之外,两者没有数据交换、没有其他的功能调用,这两个组件之间基本上互不影响。

设计这样的一个Service
需要,
继承Android SDK提供的
Service
类,重写onBind()
函数,让它返回空值;public class MyService extends Service { public MyService() { } @Override public IBinder onBind(Intent intent) { //不需要调用者和Service有功能调用,返回空值 return null; } ...... }
在
AndroidManifest.xml
中,声明新创建的Service
,<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> ...... <application ...... android:theme="@style/AppTheme"> ...... <!--声明新创建的Service--> <service android:name=".MyService" android:enabled="true" android:exported="true"></service> </application> </manifest>
使用这种Service
也很简单。假设Activity A中有个按钮start,点击之后就调用startService
;还有个按钮B-stop,点击之后就调用stopService
。
public void onClick(View v)
{
switch (v.getId())
{
case R.id.start:
{
//启动Service
Intent i = new Intent(this, MyService.class);
startService(i);
}
break;
case R.id.stop:
{
//停止Service
Intent i = new Intent(this, MyService.class);
stopService(i);
}
break;
}
}
这里运行Service的时候,是通过Intent
明确指定被运行的Service
。这种明确指定启动哪个Service
的方式叫做Service
的显示调用。与之对应的还有隐式调用,通过action
来启动。
4.2.2 Bind Service
其他组件通过调用bindService()
绑定Service
,让它运行起来;再通过调用unbindService()
解除绑定。
这种形式的Service
与调用者之间通常有功能调用或者频繁的数据交换,Service
会提供接口给其它模块调用,

设计这样的一个Service
需要,
继承Android SDK提供的
Service
类,public class MyService extends Service { public MyService() { } @Override public IBinder onBind(Intent intent) { //暂时返回空值,写下来将进行改造 return null; } ...... }
实现一个自定义的
Binder
,让它这个继承Binder
类。Binder
可以将Service
与调用者联系起来,在Binder
中提供的方法,就是Service
对外提供的方法。组件和
Service
之间的调用是通过Binder
来进行的。我们可以把Binder
看作是一个连接其他组件和Service
的桥梁,它的实现原理是什么,我们暂时不用去关心,只要知道这样用就可以了。public class MyService extends Service { ...... //创建一个自定义的Binder public class MyServiceIBinder extends Binder { //提供给其他组件调用的方法 public void function1(int param){ //调用Service中真正实现功能的方法 innerFunction1(param); } } //真正实现功能的方法 private void innerFunction1(int param) { } //创建Binder实例 private final IBinder mBinder = new MyServiceIBinder(); @Override public IBinder onBind(Intent intent) { //当组件bindService()之后,将这个Binder返回给组件使用 return mBinder; } ...... }
在
AndroidManifest.xml
中,声明新创建的Service
,<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> ...... <application ...... android:theme="@style/AppTheme"> ...... <!--声明新创建的Service--> <service android:name=".MyService" android:enabled="true" android:exported="true"></service> </application> </manifest>
其他组件使用这个Service
的时候,
创建一个
ServiceConnection
,当绑定Service
之后,在onServiceConnected()
中会得到Service
返回的Binder
;如果Service遇到异常情况退出时,会通过onServiceDisconnected()
通知绑定它的组件。private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //这里的service参数,就是Service当中onBind()返回的Binder //获取访问Service的桥梁-MyServiceIBinder MyService.MyServiceIBinder bridge = (MyService.MyServiceIBinder) service; //通过桥梁就可以调用到Service提供到函数了 bridge.function1(0); } @Override public void onServiceDisconnected(ComponentName name) { //当Service遇到异常情况退出时,会通过这里通知绑定过它的组件 } };
获得了
MyService.MyServiceIBinder
之后,我们就可以向调用普通函数那样,调用到Service
对外提供的接口函数了。需要注意的是,如果用户主动解除绑定,
onServiceDisconnected()
是不会被触发的。假设Activity A中有个按钮,点击之后就调用
bindService
;还有个按钮B,点击之后就调用unbindService
。public void onClick(View v) { switch (v.getId()) { case R.id.start: { Intent i = new Intent(this, MyService.class); bindService(i, mServiceConnection, Context.BIND_AUTO_CREATE); } break; case R.id.stop: { unbindService(mServiceConnection); } break; } }
这里同样采用的是对
Service
的显示调用。
4.2.3 混合模式
Service
并不是只能给一个组件使用,它可以同时服务于多个组件。
所以一个Service
既可以是Start Service
,也可以是Bind Service
。只要把两者需要实现的地方都实现了就行。组件A可以通过startService()
运行一个Service
,组件B可以通过bindService()
再次运行同一个Service
。

4.3 MusicService的创建
根据上面的介绍可以知道,因为MusicService
要为其它组件提供调用的接口,所以它至少是一个Bind Service
。
MusicListActivity
通过MusicService
提供的IBinder
对象,使用bindService()
方式与之建立联系,获取播放音乐、暂停音乐等等一系列与播放音乐有关的操作方法;
所以MusicService
应该按照如下方式设计,
public class MusicService extends Service {
......
public class MusicServiceIBinder extends Binder {
public void addPlayList(List<MusicItem> items) {
addPlayListInner(items);
}
public void addPlayList(MusicItem item) {
addPlayListInner(item);
}
public void play() {
playInner();
}
public void playNext() {
playNextInner();
}
public void playPre() {
playPreInner();
}
public void pause() {
pauseInner();
}
public void seekTo(int pos) {
seekToInner(pos);
}
public void registerOnStateChangeListener(OnStateChangeListenr l) {
registerOnStateChangeListenerInner(l);
}
public void unregisterOnStateChangeListener(OnStateChangeListenr l) {
unregisterOnStateChangeListenerInner(l);
}
public MusicItem getCurrentMusic() {
return getCurrentMusicInner();
}
public boolean isPlaying() {
return isPlayingInner();
}
public List<MusicItem> getPlayList() {
return null;
}
}
//真正实现功能的方法
public void addPlayListInner(List<MusicItem> items) {
}
public void addPlayListInner(MusicItem item) {
}
public void playNextInner() {
}
public void playInner() {
}
public void playPreInner() {
}
public void pauseInner() {
}
public void seekToInner(int pos) {
}
public void registerOnStateChangeListenerInner(OnStateChangeListenr l) {
}
public void unregisterOnStateChangeListenerInner(OnStateChangeListenr l) {
}
public MusicItem getCurrentMusicInner() {
return null;
}
public boolean isPlayingInner() {
return false;
}
//创建Binder实例
private final IBinder mBinder = new MusicServiceIBinder();
@Override
public IBinder onBind(Intent intent) {
//当组件bindService()之后,将这个Binder返回给组件使用
return mBinder;
}
......
}
4.4 MusicService的使用
MusicListActivity
绑定MusicService
的时候,就先定义一个ServiceConnection
:
private MusicService.MusicServiceIBinder mMusicService;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//绑定成功后,取得MusicSercice提供的接口
mMusicService = (MusicService.MusicServiceIBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
然后在onCreate()
当中,实现绑定的操作,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music_list);
Intent i = new Intent(this, MusicService.class);
//启动MusicService
startService(i);
//实现绑定操作
bindService(i, mServiceConnection, BIND_AUTO_CREATE);
}
当MusicListActivity
退出的时候,需要将MusicService
解除绑定,
@Override
protected void onDestroy() {
super.onDestroy();
......
unbindService(mServiceConnection);
......
}
这里需要注意一个细节:在绑定MusicService
之前,我们先做了一次startService()
。假如不先startService()
,那么当MusicListActivity
退出(onDestroy())的时候,MusicService
将会被销毁掉(因为调用了unbindService())。
如果用先用startService()
启动了这个服务,那么要停止它,就必须使用stopService()
了。不过在整个音乐的设计当中,我们并没有想过要让MusicService
停止工作,而是只要它运行起来了,就一直在后台待命。
至此,MusicService
的架子已经搭建完毕。
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆
的支持,谢谢:)
/*******************************************************************/