一个bound service是一个client-server接口中的server端。一个bound service容许应用组件(好比activities)bind到它,发送请求,接收响应,甚至是执行进程间通讯(IPC)。一个bound service在典型状况下,只有在它服务于另外一个应用组件时才存活,而不是在后台无限期的运行。html
这份文档向您说明了要如何建立bound service,包括在其余的应用组件中如何bind到service。然而,你也应该参考Services文档来大致地了解关于services的额外信息,好比如何在service中传送通知,设置service在前台运行,等等。java
一个bound service是一个Service类的实现,它容许其它应用bind到它并与它交互。为了给一个service提供binding功能,你必须实现onBind()回调方法。这个方法返回一个IBinder对象,该对象则定义了客户端能够用来与service进行交互的编程接口。android
如同在Services文档中讨论的那样,你能够建立一个service,既能够被started,也能够被bound。即,service能够经过调用startService()被started,从而容许service无限期的运行,也能够容许一个客户端经过调用bindService()来bind到service。编程
若是你确实容许你的service被started和bound,则当service已经被started时,系统不会在全部的客户端都unbind时销毁那个service。相反,你必须经过调用stopSelf()或stopService()来显式地中止那个service。api
尽管你一般应该实现onBind()或onStartCommand()之一,但有时须要同时实现二者。好比,一个音乐播放器可能会发现同时容许它的service无限期的运行及提供binding是颇有用的。以这种方式,一个activity能够start service来播放一些音乐,而在用户离开了应用后音乐也能够继续播放。而后,在用户回到应用时,activity能够bind到service来恢复对回放的控制。安全
请确保阅读了关于管理一个Bound Service的生命周期的部分,来了解更多关于给一个started service添加binding功能时service生命周期的信息。多线程
一个客户端能够经过调用bindService()来bind到service。当它bind时,它必须提供一个ServiceConnection的实现,其者监视与service之间的链接。bindService()将当即返回,而且没有返回值,但当Android系统建立客户端和service之间的链接时,它会调用ServiceConnection的onServiceConnected(),来传送IBinder给客户端用于和service进行通讯。并发
然而,系统只有在第一个客户端binds时才会去调用你的service的onBind()来获取IBinder。随后系统传送相同的IBinder给其它bind的客户端,而再也不次调用onBind()。app
当最后一个客户端从service unbinds时,系统将销毁service(除非service也经过startService()被started了)。dom
当你实现你的bound service时,最重要的部分是定义你的onBind()回调方法返回的接口。你能够用一些不一样的方法来定义你的service的IBinder接口,下面的小节将讨论每种技术。
当建立一个提供binding功能的service时,你必须提供一个客户端能够用于与service端交互的提供了编程接口的IBinder。有三种方法你能够用来定义这样的接口:
若是你的service是你本身的应用私有的,而且与客户端运行在相同的进程中(很常见的状况),你应该经过扩展Binder类来建立你的接口,并在onBind()方法中返回一个它的实例。客户端接收到Binder,而后就可使用它来直接访问Binder实现或Service的可用的public方法。
当你的service只是你本身的应用的一个后台工做者时,这是首选的技术。不使用这种方式来建立你的接口的仅有的理由就是,你的service被其余应用或跨越不一样的进程被用到了。
若是你须要你的接口跨进程工做,你能够借助于一个Messenger来为你的service建立一个接口。用这种方式,service定义了一个Handler,来响应不一样类型的Message对象。这个Handler是一个Messenger的基础,而后后者能够与客户端共享一个IBinder,容许客户端使用Message对象给service发送命令。此外,客户端能够定义一个它本身的Messenger以使service能够发送消息回去。
这是执行进程间通讯(IPC)最简单的方式,由于Messenger把全部的请求入队到一个单独的线程,所以你不须要为线程安全而外设计你的service。
使用AIDL
AIDL (Android Interface Definition Language)执行全部将对象分解成操做系统能够理解的元语的工做,并在进程之间处理它们来执行IPC。前面的一种技术,即便用一个Messenger,其实是以AIDL为它的底层结构的。如上面提到的,Messenger在一个单独的线程中建立一个全部的客户端请求的队列,以使service在某一时刻只接收一个请求。若是,然而,你想要你的service并行的处理多个请求,那么你能够直接使用AIDL。在这种状况下,你的service必须具备多线程的能力,而且要被构建的是线程安全的。
要直接使用AIDL,你必须建立一个定义了编程接口的.aidl文件。Android SDK工具使用这个文件来产生一个实现了接口并处理IPC的抽象类,随后你能够在你的service中扩展这个抽象类。
注意:大多数应用不该该使用AIDL来建立一个bound service,由于它可能须要多线程能力,并可能致使一个更复杂的实现。所以,AIDL对于大多数应用都是不合适的,而且这份文档中不讨论如何使用它来实现你的service。若是你确认你须要直接使用AIDL,请参考AIDL文档。
若是你的service只被本地应用用到了,而且不须要跨进程工做,那么你能够实现你本身的Binder类,来为你的客户端提供直接访问service中的public方法的能力。
注意:这种方法只有在客户端和service在相同的应用和进程中时才有效,固然这种状况很常见。例如,对于一个音乐应用,须要bind一个activity到它本身的在后台播放音乐的service,就颇有效。
这里是如何设置它:
在你的service中建立一个下述类型之一的Binder实例:
(1). 包含了客户端能够调用的public方法
(2). 返回当前的Service实例,其中具备客户端能够调用的public方法
(3). 或者,返回另外一个类的实例,其中寄宿了service,具备客户端能够调用的public方法。
在客户端中,从onServiceConnected()回调方法接收这个Binder,并使用提供的方法来调用bound service。
注意:service和客户端必须位于相同的应用中的理由是,这样客户端能够强制类型转换返回的对象,并适当地调用它的APIs。service和客户端必须处于相同的进程,因为这项技术不执行任何的跨进程处理。
好比,这儿有一个service,它经过一个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中的public方法。好比,客户端能够调用service的getRandomNumber()。
这里是一个activity,它bind到LocalService,并在button被点击时调用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; } }; }
上面的例子演示了客户端如何使用一个ServiceConnection实现和onServiceConnected()回调bind到service。下一节将提供更多关于binding到service的这一过程的信息。
注意:上面的例子没有显式地从service unbind,但全部的客户端都应该在一个适当的时间点 (好比当activity pause时) unbind。
.更多示例代码,请参考ApiDemos中的LocalService.java类和LocalServiceActivities.java类。
当你须要执行IPC时,使用一个Messenger来实现你的接口比经过AIDL来实现它要简单,由于Messenger将全部的调用入队到service,然而,一个纯粹的AIDL接口发送并发的请求给service,这将必需要处理多线程。
对于大多数应用,service不须要执行多线程,所以使用一个Messenger容许service每次只处理一个调用。若是你的service必定要是多线程的,那么你应该使用AIDL来定义你的接口。
若是你须要你的service与远端进程通讯,那么你可使用一个Messenger来为你的service提供接口。这项技术容许你在不须要使用AIDL的状况下执行进程间通讯(IPC)。
这里是一个如何使用一个Messenger的总结:
service实现一个Handler,它为来自于客户端的每一个调用接收一个回调。
客户端使用IBinder来实例化Messenger (引用了service的Handler),其可被客户端用来给service发送Message发对象。
service在它的Handler中接收每一个Message——典型地,在handleMessage()方法中。
以这种方式,客户端不调用service的"方法"。而是,客户端传送"消息" (Message 对象),service在它的Handler中接收消息。
这里有一个简单的使用了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(); } }
注意,Handler中的handleMessage()方法正是service接收传入的Message并根据what成员决定作什么的地方。
一个客户端所须要作的就是建立一个基于service返回的IBinder的Messenger,并使用send()来发送一个消息。好比,这里是一个简单的activity,它binds到service,并传送MSG_SAY_HELLO消息给service:
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。而后当客户端接收到onServiceConnected()回调时,它发送一个Message 给service,其中在send()方法的replyTo参数中包含了客户端的Messenger。
你能够参考示例MessengerService.java (service)和MessengerServiceActivities.java (client)来看一下如何提供两路消息的例子。
应用组件(客户端)能够经过调用bindService()来bind到一个service。而后,Android系统调用service的onBind()方法,它会返回一个IBinder用于与service交互。
binding是异步的。bindService()会当即返回,而不给客户端返回IBinder。要接收IBinder,客户端必须建立一个ServiceConnection的实例,并把它传递给bindService()。ServiceConnection包含一个回调方法,系统能够调用它来传送IBinder。
注意:只有activities,services,和content providers能够bind到一个service——你不能在一个broadcast receiver中bind到一个service。
于是,要在客户端中bind到一个series,你必须:
你的实现必须覆写两个回调方法:
系统调用这个方法来传送service的onBind()方法返回的IBinder。
当与service的链接意外断开时,Android系统会调用它。好比当service发生了crash或已 经被杀掉时。当client unbinds时则不会被调到。
调用bindService(),并传入ServiceConnection的实现。
当系统调用了你的onServiceConnected()回调时,你就能够开始调用service了,使用接口所定义的方法。
要从service断开链接,则调用unbindService()。
当你的客户端被销毁时,它将从service unbind,但你应该老是在你完成了与service的交互或者你的activity pause时unbind,以使得service能够在它没有被用到时关闭。(下面有更多关于bind和unbind的合适的时间的讨论。)
例如,下面的代码片断把客户端与上面经过扩展Binder类建立的service链接起来,它所必须作的一切就是把返回的IBinder强制类型转换到LocalService类,请向LocalService实例发起请求:
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()来bind到一个service。好比:
Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
bindService()的第一个参数是一个Intent,它显式地指定了要bind的service的名字(intent也能够是隐式的)。
第二个参数是ServiceConnection对象。
第三个参数是一个表示binding选项的标记。它一般应该是BIND_AUTO_CREATE,以使service在没有存活的状况下被建立出来。其它的可用的值是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或0以表示none。
这里是关于binding到一个service的一些重要的说明:
你应该老是捕获DeadObjectException一场,当链接破坏时它会被抛出。这是远程方法抛出的仅有的异常。
对象是在进程间引用计数的。
你一般应该在客户端的生命周期中对应的bring-up和tear-down的时刻成对的调用binding和unbinding。好比:
若是你只须要在你的activity可见时与service交互,则你应该在onStart()中bind,并在onStop()中unbind。
若是你想要你的activity即便已经被stopped了,也要在后台接收响应,则你能够在onCreate()中bind,并在onDestroy()中unbind。注意,这意味着你的activity须要在它运行的全部时间都使用service(即便是在后台),所以若是service在另外一个进程,则你将增长那个进程的weight,而且它变得更可能被系统杀掉。
注意:一般你不该该在你的activity的onResume()和onPause()中bind和unbind,由于这些回调在每个生命周期事务中都会发生,你应该把这些事务中发生的处理量降到最低。若是你的应用中的多个activities bind到相同的service,而且在这些activities中的两个之间有一个事务,则在当前的activity unbinds (在pause中)以后,下一个binds (在resume中) 以前可能会发生service的销毁和重建。(这种activities如何协调他们的生命周期的activity事务,在Activities文档中有描述)。
更多演示如何bind到一个service的示例代码,能够参考ApiDemos中的RemoteService.java类。
当全部的客户端都从service unbound时,Android系统会销毁它(除非它也同时经过onStartCommand()被started了)。所以,你不须要管理你的service的生命周期,若是它是纯粹的bound service的话——Android系统会基于它是否被bound到了客户端来为你管理它。
然而,若是你选择实现onStartCommand()回调方法,则你须要显式的来中止service,由于如今service被认为是被started的了。在这种状况下,service会一直运行,直到service经过stopSelf()来终止它本身,或者另外一个组件调用stopService(),而无论它是否被bound到任何的客户端。
此外,若是你的service是被started的,并接受binding,那么当系统调用了你的onUnbind()方法,若是你想要在下次客户端binds到service时接收一个对onRebind()的调用(而不是接收一个对onBind()的调用),你能够选择返回true。onRebind()返回void,可是客户端仍然在它的onServiceConnected()回调中接收IBinder。下面的图1描绘了这种生命周期的逻辑。
图1. The lifecycle for a service that is started and also allows binding.
更多关于一个started的service的生命周期的信息,请参考Services文档。
Done。