一个bound service是客户端-服务器接口中的服务器。它允许组件去绑定service,发送请求,接收响应,甚至是执行进程间的通信。一个bound service通常在服务其他组件时存在,而不是一直运行在后台。
基础
一个bound service是Service类的实现,为了绑定一个service,你需要实现onBind()回调函数。这个函数返回一个IBinder对象,定义了客户端和服务器间交互的程序接口。
客户端调用bindService()绑定一个service。这样做时,你需要提供一个ServiceConnection的实现,它监控与服务器的链接。bindService()不会返回值,不过当系统创建客户端和服务器链接时,会调用ServiceConnection中的onServiceConnected()方法来传递IBinder。
多个客户端可以同时链接到service。不过,系统只会在第一个客户端绑定时调用onBind()方法取得IBinder。其他客户端是传递的相同的IBinder,而不重新调用onBind()。
当最后一个客户端解除绑定时,系统就会销毁这个service,除非service是通过startService()启动的。
创建一个Bound Service
要能提供绑定,你就需要提供一个IBinder来和service交互,下面有三种定义通信接口的方法:
扩展Binder类
如果你的service是私有的,并且和客户端运行在同一个进程中,那么你可以扩展Binder类来实现自己的接口,onBind()返回一个这个类的实例。客户端接收到这个Binder,然后直接调用它的公共函数。
如果service仅仅为你的程序工作,那么这个是一个好的选择。
使用一个Messenger
如果你需要接口能在不同的进程中工作,你需要使用Messenger来创建接口。service定义一个Handler来响应不同的Message对象,Handler是Messenger的基础,可以分享一个IBinder给客户端,运行客户端使用Message对象发送命令到服务器。另外,客户端可以定义自己的Messenger来让service发送信息回来。
这是进程间通信最简单的方法,因为Messenger使用单线程管理请求队列,所以你不需要设计你的service为线程安全的。
使用AIDL
AIDL(Android接口定义语言)把对象分解成原始部分,让系统能够理解并安排他们进行进程间通信。前面说的使用Messenger的方法就是一个简单的AIDL实现,使用Messenger的话,service一次只会接收到一个请求,如果你想同时处理多个请求,那么你可以直接使用AIDL,这种情况下,你的service必须支持多线程,并且要实现线程安全,更多AIDL使用看下一个教程。
扩展Binder类
如果service只是本地使用,不需要跨进程工作,那么使用自定义的Binder类就可以了。
提示:这种方式只能在客户端和服务器在一个程序和进程中才能使用,例如,一个音乐程序绑定一个activity到自己的service然后在后台播放音乐。
那么是实现的步骤:
- 在service中创建一个Binder的实例:
a. 包含客户端可以调用的公共函数。
b. 返回当前Service实例,实例包含客户端可以调用的公共函数。
c. 或者返回service内部类的实例,包含可调用的公共函数。
- 从onBind()中返回Binder的实例。
- 在客户端的OnServiceConnected()取得Binder,调用service中的公共函数。
实例:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
LocalBinder提供getService()方法来让客户端取得当前LocalService的实例。然后用这个实例就可以调用它的getRandomNumber()方法。
下面的客户端activity的实现:
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Called when a button is clicked (the button in the layout file attaches to
* this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
使用Messenger
如果要实现进程间通信,就需要使用Messenger了。
下面是使用Messenger的概述:
- service实现一个Handler接收客户端的回调。
- Handler被用来创建一个Messenger对象。
- Messenger创建一个IBinder,在onBind()方法中返回给客户端。
- 客户端使用IBinder初始化Messenger,然后使用它发送Message到服务器。
- 服务器接收到Message,在Handler中处理这些Message。
这种方法中客户端不需要调用service的方法,而是让Handler接收信息,然后处理。
下面是service中的实现:
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
Handler中的handleMessage()方法接收客户端传送过来的Message,然后基于what参数做比较,从而执行对应的工作。
下面是简单的客户端activity的实现:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
上面的例子没有实现服务器返回信息给客户端的功能,如果你想收到服务器的响应的话,你需要在客户端创建一耳光Messenger,然后在send()方法中使用replyTo参数传递Messenger到服务器。
绑定到一个Service
客户端可以使用bindService()来绑定到一个service。系统就会调用onBind()方法来响应bindService()的调用。然后返回一个IBinder给客户端。
绑定时异步的,bindService()会立即返回,当时并没有返回IBinder给客户端,要接收到IBinder,客户端必须创建一个ServiceConnection的实例,然后传递给bindService()方法。ServiceConnection包含接收IBinder的回调函数。
提示:只有activity,service,content provider可以绑定一个service,你不能使用broadcast receiver绑定一个service。
那么,绑定一个service你需要做:
- 实现ServiceConnection。
包括重写里面的两个方法:
onServiceConnected()
系统调用它来取得onBind()返回的IBinder。
onServiceDisconnected()
在service被意外丢失时调用,比如service自己崩溃了,或者被系统杀掉了。这个方法在客户端解除绑定时不会被调用。
- 调用bindService()方法传递ServiceConnection的实现。
- 当系统调用onServiceConnected方法时,你可以开始使用服务器的方法。
- 使用unbindService()来断开service连接。
当客户端被销毁后,会从服务器解除绑定,不过你应该交互完成或者activity暂停时解除绑定,这样能很好的关闭不需要的service。
例如,下面是绑定的代码片段:
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Because we have bound to an explicit
// service that is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "onServiceDisconnected");
mBound = false;
}
};
使用ServiceConnection,客户端可以使用bindService()绑定到service:
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
补充提示
下面是一些重要的提示:
- 当连接被破坏时需要抛出一个DeadObjectException异常。这是远程函数唯一会抛出的异常。
- 对象是夸进程引用的。
- 绑定和解除绑定应该是成对出现的。例如:
a. 在activity中在onStart()绑定,在onStop解除绑定。
b. 如果你系统activity在被停止后还能接收响应,你可以在onCreate()中绑定,在onDestroy()中解除绑定。
提示:你不应该再onResume()中绑定,在onPause()中解除绑定,因为这两个方法发生在生命周期的过渡阶段,时间比较短,很难保证进程的启动。同样的,如果有多个activity绑定到相同的service,那么在第二个activity绑定前,当前的activity解除绑定可能会导致service被销毁和重建。
管理Bound Service的生命周期
如果service仅仅是一个bound service,那么系统自动管理它的生命周期。不过,如果你选择实现onStartCommand()回调方法,你必须显式的停止service,因为这个时候service是被启动的,它一直运行,直到你来停止它。
另外,如果你的service是被启动的,然后接受了绑定,那么当系统调用你的onUnbind()方法时,你可以选择返回true的话,就是说下一次客户端绑定这个service时,是调用onRebind()来替换调用onBind()。onRebind()返回空,但是客户端仍然在onServiceConnected()中接受IBinder。下图展示了这个逻辑关系: