一个Bound Service相当于cient-server中的server。Bound Service允许其它组件(比如activity)绑定到Service中,发送请求,接收响应,甚至进行内部进程通信。Bound Service是有在有其它组件绑定它时才运行,一旦没有组件绑定时,系统将会摧毁这个Bound Service。
这部分展示如何创建一个Bound Service,包括如何从其它app组件绑定一个Service。可以结合Service部分。
The Basics (基础)
Bound Service通过对Service的继承来允许其它app的组件能够绑定到这个Service并和这个Service交互。为了能够被其它组件绑定,你必须实现onBind()函数,在这个函数中返回一个IBinder对象,这个IBinder定义了编程接口用于client与Service交互。
一个client可以通过调用bindService()来绑定一个Service。当这样做后,这个client必须实现ServiceConnetion类,这个类监视与Service的连接。bindService被调用后马上返回,当系统创建client和Service的连接时,ServiceConnection中的onServiceConnected()函数被调用,来派发IBinder这个对象。
多个client可以同时连接到一个service。但是系统仅仅在第一次被绑定时调用onBind()函数火气IBinder。在接下来的绑定中,系统都是将相同的IBinder床给其它clients,不会在调用onBind()函数。
当最后一个client取消绑定时,系统将会销毁这个service(除非这个service是通过startService被启动)。
当实现bound service时,最重要的一点是定义onBind()返回的接口。有几种不同的方式定义这接口,下面将一一讨论。
Creating a Bound Service (创建一个bound Service)
当创建一个bound Service时,你必须提供一个IBinder接口,client可以通过这个接口来和Service交互,有三种定义IBinder的方式。
Extending the Binder class (继承Binder类)
如果你的Service仅仅在你的app可用,而且和client运行在同一个进程中,你应该通过继承Binder类来创建你的Binder。client接收到这个Binder后可以直接访问Binder或Service的公共函数。
这非常适合当你的Service仅仅是你app的一个后台worker。但是当你的Service可被其它app使用或者在不同的进程运行时,你即不能用上面的方法构造Binder。
Using a Messenger (使用Messenger)
如果你的接口需要工作在不同的进程中,你可以在这个Service中创建一个Messenger接口。这种情况下,Service将会定义一个Handler对象处理不同的Message对象。client就可以通过Message 对象来对Service发送命令,在client中也可以自己定义一个messager来接收Service发送的Message。
Using AIDL (使用AIDL)
AIDL(Android interface definition language android接口定义语言)将所有对象都分解成操作系统可以理解的原型然后marshall这些原型在多进程间通信。上面使用Messager就是基于AIDL的,指示使用了Messenger作为底层机构。正如前面提到,Messenger在一个单线程中创建一个队列用来保存所有client的请求。所以Service在每次只接收一个请求。如果你想让你的Service每次接收同时接收多个请求,可以直接使用AIDL。在这种情况下,你的Service必须支持多线程操作并保证安全。
可以创建一个.aidl文件定义相关的编程接口来直接使用AIDL。android SDK将会用这个文件来创建一个实现这个接口的抽象类来处理IPC,你可以在你的Service中继承这个抽象类。
注意:大多数app最好不要使用AIDL来创建bound Service,因为这需要多线程操作,实现起来复杂。因此本文档没有详细介绍AIDL。若你真想使用AIDL,可以详见AIDL部分。
Extending the Binder class (继承Binder类)
如果你的Service仅仅本地app可以使用,并且不用跨进程,那么你可以实现你自己的Binder类一边client可以通过这个Binder来访问你Service的公共函数。
注意:只有当client和Service处于同一个app和进程时,上面这个才能工作,比如,当一个音乐app需要一个activity绑定到在后台播放音乐的Service中。
下面展示设置:
1. 在你的Service中创建一个Binder实例:
包含client可以调用的公共函数。
返回当前Service实例,通过这个实例调用Service的公共函数。
返回这个Service拥有的其它activity实例,通过这个实例调用其它activity的公共函数。
2. 在onBind()函数中返回这个Binder实例
3. 在client的onServiceConnected()回调函数中获取这个Binder实例,使用这个实例来调用Service的公共函数。
注意:之所以要求Service和client在同一个app中是为了client能够投射返回的对象并正确的调用它的API。另一个要求是client和Service必须在同一个进程中,因为这个技术不执行任何marshalling与进程间。
比如,下面的client可以通过返回的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()函数一边clients用来获取当前的LocalService实例。这样client就可以调用Service中的公共函数。例如:client可以调用serviced的getRandomNumber()。
下面的activity绑定到LocalService中,当单击一个按钮时调用Service中的getRandomNumber()函数。
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;
}
};
}
上面例子展示client通过实现ServiceConnection来绑定到Service中。下部分将提供更多关于绑定到service的信息。
注意:上面例子中没有显式的解除去service的绑定,但是每个activity都会在一段时间后解除绑定(比如当activity暂停)。
Using a Messenger (使用Messenger)
如果你的service需要跟远程进程相互通信,那么你可以使用Messenger作为你service的提供的接口。这能让你在不实用AIDL的情况下进行内部进程通信(IPC)。
下面总结如何使用Messenger:
1. 在service中实现一个Handler,用来接收client的每次调用。
2. 使用Handler来创建Messenger对象(是Handler的一个引用)
3. Messenger创建一个IBinder,用于service在onBind()函数中返回给client。
4. client使用IBinder来实例化一个Messenger,用于发送Message对象到service。
5. 在service的handleMessage()函数中接收每个Message。
在这种方式下,client没有调用service的函数,而是通过发送message对象到service中。
下面是一个使用Messenger接口的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();
}
}
service在Handler里面的handleMessage()函数根据Message的类别来处理消息。
client需要做的是根据从service中返回的IBinder构建一个Messenger对象,通过send()发送消息。下面的例子有一个Activity绑定到上面的service中,并发送一个MSG_SAY_HELLO的消息。
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;
}
}
}
注意到上面的例子并没有展示service如何回到client,如果你想service能够回到client,那么你必须在client中创建一个Messenger。当client的onServiceConnected()回调函数被调用后,它发送一个包含client的Messenger的消息到service中。
你可以在例子MessengerService.java(service)和MessengerServiceActivities.java(client)中看到如何实现来回发送消息。
Binding to a Service (绑定一个Service)
app的组件(client)可以通过调用bindService()函数绑定一个Service,之后android系统捡回调用Service的onBind()函数,在这个函数中返回给client一个IBinder,用来与Service交互。
绑定到Service是异步的。bindService()函数马上返回,但不是马上返回一个IBinder给client。为了拿到IBinder,client必须创建一个ServiceConnection的实例并将这个传入bindService中。在ServiceConnection中含有一个回调函数,系统将通过这个回调函数返回从service的IBinder。
注意:只有Activity, service, ContentProvider可以绑定到一个service中,broadcast receiver不能帮到到service中。
所以为了绑定到一个service中,你必须:
1. 实现ServiceConnection
在这个类的实现中必须覆盖两个回调函数
onServiceConnected():系统调用这个函数来返回从service过来的IBinder。
onServiceDisconnected():当与service的连接中断时(比如当service发生故障或者被销毁时),系统会调用这个函数。但是当client解除绑定时,系统不会调用这个函 数。
2. 在client中调用bindService(),在函数参数中传入ServiceConnection的实例。
3. 当系统调用你的onServiceConnected()回调函数时,你可以调用Service中国的函数。
4. 调用unbindService()函数来解除绑定,当你的client被销毁时,会自动解除与service的绑定,但通常当你已经完成和service交互你最好解除绑定,或者当你的Activity被暂停时,你也最好解除绑定,因为这样对应的service在无人使用时可以被销毁。
上面的例子通过返回一个继承自Binder的IBinder到client中,通过这个IBinder获取service的实例,这样就可以通过这个实例来调用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;
}
};
在bindService()函数中传入一个ServiceConnection的实例
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
在bindService的第一个参数是Intent对象,显式的指定了要绑定的service。
第二个参数是ServiceConnection对象。
第三个参数是一个标识,可以有BIND_AUTO_CREATE, BIND_DEBUG_UNBIND 和BIND_NOT_FOREGROUND等参数。
Addtional notes (注意事项)
以下是绑定到service的注意事项:
1. 你应该一直捕获DeadObjectException错误,这个是当连接中断是被抛出。
2. Objects are reference counted across processes.
3. 可以根据你的需要在合适的地方进行绑定service和解除绑定。比如当你需要在你的Activity是可见的时候与service交互,那么你可以在onStart()中进行绑定,在onStop()中解除绑定,如果你希望当你的client被停止时还能接收service的回答,那么你可以在onCreate()中绑定service,在onDestroy()中解除绑定。这表面你希望在client的整个生命过程中使用service,如果这个service实在其它的进程中,那么你将提高这个进程的权重,系统更有可能会销毁这个进程。
注意:在Activity的onResume()和onPause()中国你不要绑定和解除绑定到一个service,因为这两个函数发生在状态转换的时候,在这两个函数中执行的代码应尽量少。另外当在一个app中有多个Activity绑定到一个service中,那么当发生两个Activity的状态转换时,系统将有可能由于一个Activity解除绑定而销毁service,而下个Activity有可能还要绑定这个service(这样就销毁了又重建),没必要。
更多信息详见ApiDemos中的RemoteService.java
Managing the Lifecycle of a Bound Service (管理Bound Service的生命周期)
当一个Service没有client绑定时,系统将会销毁这个Service(除非这个Service是通过onStartCommand()启动的)。这样对于bound Service你就不用去管理它的生命周期了。
但是如果你实现了onStartCommand()函数,那么你必须显式的停止这个Service,因为这个Service是被started的(而不是通过被绑定而创建)。在这种启动模式下,这个Service将会一直运行,直到自身调用stopSelf()或者其它的app组件调用stopService()而停止。
如果你的Service是被started而且接受绑定,那么当系统调用onUnbind()函数时,你可以在这个函数中返回true表示这个Service的onRebind()函数可以在下次client绑定Service时被调用(而不是调用onBind()函数)。在onRebind()中返回void,但是client仍然会在onServiceConnection()中接收到一个IBinder对象,下图显示了这个逻辑过程: