一、基础
其他应用组件可以绑定到Bound Service上,可以向其发送请求并获得响应,甚至可以通过它进行线程间通信(IPC)。Bound Service并不是在后台无限运行的,当没有组件绑定在它上面的时候,它就会被系统销毁(除非它还被startService()启动了)。
一个service要实现绑定功能,必须要实现onBind()方法,该方法返回一个IBinder,是客户和service交互的接口。
客户调用bindService()绑定到service上;注意,客户必须提供一个ServiceConnection的实现,当客户与service建立连接时,ServiceConnection中的onServiceConnected()方法会被调用,该方法接收由onBind()返回的IBinder,客户就是通过这个IBinder跟service通信。
多个客户可以同时绑定到一个service上。但是,系统只在第一个客户绑定时调用onBind()方法、并返回一个IBinder;对于其他的客户,系统会向他们返回同一个IBinder,但是不会再调用onBind()方法。
二、创建Bound Service
当创建一个Bound Service时,必须提供一个IBinder接口。
定义IBinder有以下三种方式:
*继承Binder类(Binder实现了IBinder接口)
如果service只在你自己的应用里面使用、并且与客户运行在同一进程中,就应该使用这种方法创建IBinder;客户获得该Binder的实例后,就可通过它直接使用Binder中、甚至service中的方法。
*使用Messenger
如果IBinder接口要在不同进程间工作,可以用Messenger创建该接口。用这种方式,需要在service中定义一个Handler,用于处理Message;然后用这个Handler创建一个Messenger,调用该Messenger的getBinder()方法可得到一个IBinder,客户就可通过该IBinder向service发送Message了。另外,客户也可以定义自己的Messenger,这样,service就可以向客户返回Message了。
这种方式是实现IPC的最简单的方法,因为Messenger将所有请求放进一个线程队列中,因此不需要考虑线程安全的问题。
*使用AIDL(Android Interface Definition Language)
AIDL是另一种实现IPC的方式。实际上,上面介绍的使用Messenger的方法,就是基于AIDL实现的。如上所述,Messenger会把所有的请求放进一个线程队列中,因此service每次只会接收到一个请求;如果service要同时处理多个请求,就可以使用AIDL实现。当然,使用AIDL时要求service能够处理多线程并需要考虑线程安全的问题,实现起来比Messenger方法复杂的多,所以可以选择时,优选Messenger方法。
下面分别介绍前两种方法的具体实现:(关于AIDL的具体使用,见Android学习--Service之AIDL)
方式一:继承Binder类
注意,该方法只适用于客户与service处在同一应用和进程的情况。
创建过程如下:
1、在service中创建Binder的实例,该Binder应满足下列情形之一:
*包含一个可被客户调用的方法
*返回当前service的实例,且该service中包含能被客户调用的方法
*返回service持有的某个类的实例,该类包含可被客户调用的方法
2、在onBind()方法中返回这个Binder实例;
3、在客户端的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的实例,这使得客户可以调用service中的方法(如getRandomNumber()方法)。
下面这个activity绑定到了LocalService上,并且当其中的button被点击时调用LocalService的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; } }; }
(上面的例子中没有明确何时解除对service的绑定,这是不合适的,所有客户都应该在合适的时间解除绑定。)
方式二:使用Messenger
如果service要与其他进程同信(IPC),可使用此方法。(如果service同时还要执行多线程任务,则应该使用AIDL)
使用步骤如下:
1、在service中创建一个Handler(用于接收客户的请求);
2、用Handler的实例创建一个Messenger(它会引用到上述Handler);
3、调用Messenger的getBinder()方法生成一个IBinder,并在onBind()方法中返回这个IBinder对象;
4、客户用得到的IBinder对象实例化一个Messenger(它会引用到service中的Handler),客户可以用这个Messenger向service发送Message;
5、service在其Handler的handleMessage()方法中接收客户发过来的Message。
示例代码如下:
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返回的IBinder创建一个Messenger,并调用其send()方法发送消息,如下:
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如何对客户进行响应。如果想让service进行响应,客户需要创建一个Messenger对象,当向service发送Message时,将创建的Messenger赋值给Message的replyTo成员变量;这样,service响应的消息就会被放到这个Messenger对象中。
三、绑定service
注意,只有Activity、Service、Content Provider可以绑定到service上,不能在BroadcastReceiver中绑定service。
绑定过程是异步的。bindService()会马上返回,但是客户并不是通过该函数得到service返回的IBinder;要得到IBinder,客户必须创建一个ServiceConnection实例,并把它传递给bindService(),系统会调用ServiceConnection中的onServiceConnected()函数,用于传递service返回的IBinder。
因此,客户要绑定到service,必须:
1、实现ServiceConnection,并复写以下两个方法:
onServiceConnected():系统会调用此方法传递由service的onBind()返回的IBinder。
onServiceDisconnected():当跟service的连接被意外终止时被调用(例如,service崩溃或被意外杀死);如果连接的终止是由客户端接触绑定引起的,该方法不被调用。
2、调用bindService()方法,并向它传递ServiceConnection的实现;
3、当系统调用onServiceConnected()方法得到IBinder后,客户就可以通过该IBinder中的相应方法呼叫service;
4、调用unbindService()解除绑定。(当客户被销毁时,系统会解除其对service的绑定,但是不应该依靠这种方式解除绑定;通常应该在完成了与service的交互、或绑定的activity处于paused状态时,调用unbindService()解除绑定、关闭service)
具体的步骤可参见上面"创建Bound Service"部分的示例代码;
但需注意bindService (Intent service, ServiceConnection conn, int flags)函数的三个参数:
*service:是一个Intent,用于指定绑定到哪个service
*conn: 创建的ServiceConnection实例
*flags: 整数,指示绑定的方式,通常设为BIND_AUTO_CREATE,意思是,绑定时如果该service还未被创建,就创建它;还可以设为以下的值: 0, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY(各个值得具体含义参见关于"Context"抽象类的文档)。
其他的注意事项:
1、当与远程service通信时,要记得捕获DeadObjectException,该异常在连接意外终止时抛出。如上述ActivityMessager代码中的sayHello()方法所示。
2、Objects are reference counted across processes.(不理解!)
3、应当成对的进行绑定和解除绑定的操作、并与客户的生命周期方法相对应,例如:
*如果只是在activity可见时才与service交互,就应该在activity的onStart()方法中绑定service,在activity的onStop()方法中解除绑定;
*如果要activity在不可见状态时仍能接收service的响应,应该在activity的onCreate()方法中绑定service,在activity的onDestroy()方法中解除绑定。(注意,这种情形意味着 activity的整个生命周期中都会用到service,如果该service处在另一个进程中,就会增加该进程的负担,导致系统杀死它的可能性提高)
注意,通常不应该在activity的onResume()或onPause()方法中进行绑定service或解除绑定的操作,因为这两个函数被调用的比较频繁,应该在它们当中执行尽量少的操作。
四、管理Bound Service的生命周期
当所有客户对其解除绑定时,系统会自动将其销毁(除非该service还被onStartCommand()启动了),因此对这种纯净的Bound Service,不需要对它的生命周期进行管理。但是,如果在该service中实现了onStartCommand()方法,该service会被认为是被"started";这时,即使没有客户绑定在这个service上,也必须调用stopSelf()或其他组件调用stopService()将其结束,这样该service才会被系统回收。(因此,在创建Bound Service时,如无必要,不要实现onStartCommand()方法。)
另外,在一个被绑定的service同时被"started"时,可以选择让onUnbind()返回true,这样的话,再有客户绑定到该service上时,会调用其onRebind()方法(不再调用onBind()方法);虽然onRebind()返回值为void,客户仍然能从onServiceConnected()方法中得到IBinder对象。如下图中所示:
Figure 1. The lifecycle for a service that is started and also allows binding.