一个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类
使用一个Messenger如果你的service是私有的,并且和客户端运行在同一个进程中,那么你可以扩展Binder类来实现自己的接口,onBind()返回一个这个类的实例。客户端接收到这个Binder,然后直接调用它的公共函数。如果service仅仅为你的程序工作,那么这个是一个好的选择。
使用AIDL如果你需要接口能在不同的进程中工作,你需要使用Messenger来创建接口。service定义一个Handler来响应不同的Message对象,Handler是Messenger的基础,可以分享一个IBinder给客户端,运行客户端使用Message对象发送命令到服务器。另外,客户端可以定义自己的Messenger来让service发送信息回来。这是进程间通信最简单的方法,因为Messenger使用单线程管理请求队列,所以你不需要设计你的service为线程安全的。
扩展Binder类AIDL(Android接口定义语言)把对象分解成原始部分,让系统能够理解并安排他们进行进程间通信。前面说的使用Messenger的方法就是一个简单的AIDL实现,使用Messenger的话,service一次只会接收到一个请求,如果你想同时处理多个请求,那么你可以直接使用AIDL,这种情况下,你的service必须支持多线程,并且要实现线程安全,更多AIDL使用看下一个教程。
如果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。下图展示了这个逻辑关系: