绑定服务的实现
什么时候需要使用绑定服务呢?我的理解是调用者在启动完服务后,还需要以后与服务打交道,还要控制服务,而不是像启动服务一样任服务自由自在不受控制。常见的一个场景,Service播放音乐,界面上有上一曲,下一曲、暂停、播放等按钮,通过这些按钮还可以控制Service操作,这种情景就是使用的绑定,本篇博客的例子也将实现一个简单的demo。
1.定义服务
绑定服务,需要重写onBind()方法,并且返回一个IBinder对象,该IBinder客户端再绑定服务成功后可以获取一个该对象,因此客户端可以通过IBinder来控制服务。IBinder是一个接口,一般我们不会直接实现该接口,而是会继承Binder类,Binder类实现了IBinder类。
下面是该播放服务的示例:
- 播放音乐服务
*/
public class PlayMusicService extends Service {
public PlayMusicService() {
}
public class PlayMusicBinder extends Binder {
/**
* 播放音乐
*/
public void play() {
playMusic();
}
/**
* 暂停播放
*/
public void pause() {
pauseMusic();
}
/**
* 暂停播放
*/
public void stop() {
stopMusic();
}
}
private PlayMusicBinder mBinder = new PlayMusicBinder();
private MediaPlayer mediaPlayer;
@Override
public void onCreate() {
super.onCreate();
mediaPlayer = MediaPlayer.create(this, R.raw.diamonds);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null)
mediaPlayer.release();
}
private void playMusic() {
mediaPlayer.start();
}
private void pauseMusic() {
mediaPlayer.pause();
}
private void stopMusic() {
mediaPlayer.stop();
} }
其中PlayMusicBinder继承自Binder类,并且提供了三个接口,分别是播放音乐、暂停音乐以及停止播放;在onBind()方法中返回这样一个实例。播放音乐用到了MediaPlayer,音乐文件直接直接放在了res/raw/目录下。至此,服务端就完成了,主要工具就是编写一个Binder类提供给客户端操作的接口。
从上面可以看到,定义服务端主要包括两步:
- 扩展Binder类,实现接口
- 在onBind()方法返回一个IBinder实例
2.定义调用者(Activity)
在定义完服务端后,再来定义客户端,bindService方法中第二个参数是一个ServiceConnection接口,当服务连接成功以及失去连接时会分别回调两个方法。下面是Activity的定义:
public class PlayMusicActivity extends AppCompatActivity {
private PlayMusicService.PlayMusicBinder musicBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//在服务连接成功后保存PlayeMusicBinder对象,控制服务端
musicBinder = (PlayMusicService.PlayMusicBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
musicBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_music);
}
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, PlayMusicService.class), connection, Service.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
public void playMusic(View view) {
if (musicBinder != null)
musicBinder.play();
}
public void pauseMusic(View view) {
if (musicBinder != null)
musicBinder.pause();
}
public void stopMusic(View view) {
if (musicBinder != null)
musicBinder.stop();
} }
从上面的代码可以看出,在onStart中绑定服务,一旦服务绑定成功,则保存PlayMusicBinder对象。界面上有三个按钮,分别是播放、暂停和停止,接下来就可以控制后台服务播放音乐了。
从上面可以看到,定义客户端主要有三步:
- 定义ServiceConnection对象,在onServiceConnect回调中保存IBinder对象
- 绑定服务
- 使用onServiceConnect回调中保存的IBinder对象操作Service
在服务中扩展Binder这种方式适用于服务端和客户端在同一个进程中,那么如果客户端和服务端在不同进程中,那么该如何实现了?也就是通常意义上的IPC,IPC的方式有很多,比如管道、共享内存、Socket等等,但是在Android中有一种特殊的机制:Binder机制。下面首先介绍Binder机制的两种表现形式,Messenger和定义AIDL接口。下面分别就这两种方法就行介绍。
Messenger
Messenger对Binder进行了封装,适用于服务端单线程的情况,因为服务端会定义一个Handler用于顺序处理客户端发送过来的Message,在服务的onBind中返回内部的Binder。
Messenger用于发送消息,Handler用于处理消息,一般我们使用服务端作为处理消息,所以服务端需要Messenger和Handler,并且在onBind方法中返回Messenger内部的Binder,这样客户端就可以根据绑定服务后得到Messenger,可看做是服务端的Messenger,然后就可以通过它来发送消息了;那么如果服务端会在处理完客户端的消息后回复客户端,并且客户端也需要处理这个消息,那么该如何实现呢?
根据上面的分析,既然服务端需要回复客户端,也就得拿到客户端的Messenger,然后客户端需要处理消息,也需要一个Handler,所以说客户端也需要一个Messenger和一个Handler。下面我们以一个例子来详细说明,客户端向服务端发送两个整形数,服务端将和返回给客户端。
1.服务端的定义
/**
-
使用Messenger,适合应用与服务在不同进程中
*/
public class MessengerService extends Service {//Handler用于处理客户端发送来的消息,不存在线程安全问题
private Handler mHandler = new Handler() {@Override public void handleMessage(Message msg) { switch (msg.what) { case 0: int a = msg.arg1; int b = msg.arg2; int sum = a + b; //得到客户端的Messenger,回复消息 Messenger replyTo = msg.replyTo; Message message = Message.obtain(); message.what = 1; message.arg1 = sum; try { replyTo.send(message); } catch (RemoteException e) { e.printStackTrace(); } break; } }
};
private Messenger messenger = new Messenger(mHandler);public MessengerService() {
}@Override
public IBinder onBind(Intent intent) {
//返回Messenger内部的Binder
return messenger.getBinder();
}
}
从上面的代码可以看到,服务端主要包括三步:
-
定义Handler用于处理消息
-
根据Handler声明Messenger对象
-
在onBind方法中返回Messenger的Binder
2.客户端的定义
在定义完服务端之后,我们再来看下客户端的定义,客户端主要完成绑定服务,发送数据以及接受数据的功能。如下:
public class MainActivity extends AppCompatActivity {
private TextView resultShowTv;
private Messenger messenger;
private ServiceConnection connection = new ServiceConnection() {@Override public void onServiceConnected(ComponentName name, IBinder service) { //得到服务端的Messenger,发送消息 messenger = new Messenger(service); Message msg = Message.obtain(handler, 0, 5, 10); msg.replyTo = replyTo;//将客户端的Messenger对象赋给replyTo字段 try { messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); resultShowTv = (TextView) findViewById(R.id.result_show_tv);
}
//客户端中接受消息的Messenger
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
int sum = msg.arg1;
resultShowTv.setText(String.valueOf(sum));
break;
}
}
};
private Messenger replyTo = new Messenger(handler);、
public void messenger(View view) {
Intent bindService = new Intent(this, MessengerService.class);
bindService(bindService, connection, BIND_AUTO_CREATE);
}
}
从上面的代码可以看到,客户端主要有三步:
- 编写ServiceConnection类,在onServiceConnect回调中得到服务端的Messenger,一旦得到了该对象,就发送Message对象,并且将客户端这边的Messenger带了过去
- 编写客户端处理消息的Handler以及Messenger
- 绑定服务