Bound Services(绑定服务 )
绑定服务是有一个接口可以在客户端和服务端之间帮助通信服务。绑定服务允许组件(比如activities)绑定它,组件可以发送请求,收到响应,并且可以与service在进程之间通讯。一个绑定服务通常的生命周期与它服务于的其他应用程序组件一同存在,不会在后台一直运行下去。
这个文档告诉你如何去创建一个绑定服务,包括如何绑定服务。
The Basics
绑定服务是允许其他的组件绑定它,并与它进行交互。让一个service有绑定功能,就必须实现onBind()方法,这个方法返回IBinder 对象,这个IBinder对象定义了客户端如何与服务端通信。
Binding to a Started Service
As discussed in the Servicesdocument, you can create a service that is both started and bound. That is, the service can be started by callingstartService()
, which allows the service to run indefinitely, and also allow a client to bind to the service by calling bindService()
.
If you do allow your service to be started and bound, then when the service has been started, the system does not destroy the service when all clients unbind. Instead, you must explicitly stop the service, by callingstopSelf()
or stopService()
.
Although you should usually implement either onBind()
oronStartCommand()
, it's sometimes necessary to implement both. For example, a music player might find it useful to allow its service to run indefinitely and also provide binding. This way, an activity can start the service to play some music and the music continues to play even if the user leaves the application. Then, when the user returns to the application, the activity can bind to the service to regain control of playback.
Be sure to read the section aboutManaging the Lifecycle of a Bound Service, for more information about the service lifecycle when adding binding to a started service.
客户端可以调用bindService()方法与service进行绑定。当已经绑定,它必须提供ServiceConnection的实现,可以监控与service的连接。 bindService()方法会立刻返回,并且没有值,但是,当系统在客户端和服务端创建链接时,ServiceConnection上的onServiceConnected()会调,得到IBinder对象。
对个客户端可以同时连接一个service。然而,系统第一次调用onBind()的时候才会返回IBinder对象,系统将这个IBinder对象返回给绑定它的客户,不会重复调用onBind().
当没有客户绑定它时,系统就会销毁服务.
在实现绑定服务的时候,定义IBinder接口是非常重要的。定义这个接口有多种方式,下面将会讨论。
Creating a Bound Service
创建绑定服务,必须提供IBinder接口,有三种方式定义这个接口:
- Extending the Binder class
- 如果这个service只是为当前应用服务,并且运行在相同的进程里,创建的接口必须继承Binder类,并且在onBind()方法中返回。客户可以通过它得到Binder对象,通过这个对象可以直接访问共有函数。
当service仅仅为自己的应用服务,这是首选技术。
- Using a Messenger
- 如果需要接口在不同的进程间使用,可以用Messenger 为service类创建接口。用这种方式, service对处理不同的Message 对象定义了一个Handler. 这个Handler是Messenger的基础,可以与客户分享IBinder,允许客户用message发送命令到service上。补充点,客户端可以自定义Messenger,因此service可以发送消息回来。
这是在进程间通信最简单的方式。因为Messenger队列所有的请求发生在一个线程上,因此不需要考虑service的线程安全。
- Using AIDL
- AIDL (Android Interface Definition Language) 可以把对象分解成系统能够识别的单元,已达到在进程间通信的能力。在前面的技术中,使用的messenger,实际上是以AIDL作为基础的结构。如上所述,Messenger在一个线程中创建了所有客户请求的一个队列。如果你想service能偶同时处理多个请求,那么就可以直接用AIDL. 在这种案例中,service 必须要有处理多线程的能力,并且是线程安全的。
要想直接使用AIDL,必须创建定义程序的接口文件,后缀是.aidl。Android SDK工具可以通过这个文件生成一个实现了这个接口和处理IPC的抽象类,自定义的service需要继承自这个抽象类。
Note: 大部分的应用不需要使用AIDL创建绑定service,因为它可能需要多线程功能和实现上更加复杂。就AIDL而论,它不适合大部分的应用,并且这个份文档不会讨论AIDL的使用。如果需要直接使用AIDL,可以查阅AIDL文档。
Extending the Binder class
如果service仅仅是用在本应用,并且不需要在进程间通讯,这样实现用户端可以直接访问的Binder类,它可以帮助客户端访问service里面的共有方法。
Note: 这个service必须是在同一个应用和进程中才能工作,这种方式也是最常见的。例如,在音乐应用中,activity需要和他它自己service绑定,以便能在后台播放音乐。
以下是设置的方法:
- 在service中,创建一个Binder的实例,这个Bind必须要满足:
- 包括客户端可以调用的共有方法
- 返回当前service实例
- 或者,返回另一个类的一个实例
- onBind()方法要返回Binder对象
- 在客户端,从onServiceConnected()获得Binder对象,调用提供的方法操作绑定服务。
Note: service与client必须在同一个应用的原因是,client可以将返回对象强制转换成合理的对象,并且调用它的API.service和client必须在同一个进程中,这种方式不能处理进程间的信号处理。
例如,这里有service,client可以通过Binder调用它里面的方法:
publicclassLocalServiceextendsService{ // Binder given to clients privatefinalIBinder mBinder =newLocalBinder(); // Random number generator privatefinalRandom mGenerator =newRandom(); /** * 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. */ publicclassLocalBinderextendsBinder{ LocalService getService(){ // Return this instance of LocalService so clients can call public methods returnLocalService.this; } } @Override publicIBinder onBind(Intent intent){ return mBinder; } /** method for clients */ publicint getRandomNumber(){ return mGenerator.nextInt(100); }}
client 通过LocalBinder 的共有方法(getService())可以得到LocalService。client通过这个service可以调用它的共有方法。例如,clients可以调用getRandomNumber().
下面有个activity,它与LocalService
绑定了,当双击button的时候调用了getRandomNumber()
:
publicclassBindingActivityextendsActivity{ LocalService mService; boolean mBound =false; @Override protectedvoid onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protectedvoid onStart(){ super.onStart(); // Bind to LocalService Intent intent =newIntent(this,LocalService.class); bindService(intent, mConnection,Context.BIND_AUTO_CREATE); } @Override protectedvoid 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) */ publicvoid 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() */ privateServiceConnection mConnection =newServiceConnection(){ @Override publicvoid 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 publicvoid onServiceDisconnected(ComponentName arg0){ mBound =false; } };}
上面的实例展示client如何实现ServiceConnection和实现ServiceConnection的回调方法onServiceConnected()
与service绑定.
Note: 上面的例子并没有显示的取消和service的绑定,所有的clients必须在合适的时候解除绑定(activity停止)。
想了解更加详细的例子代码,可以在APiDemo里面查找LocalService.java 和 LocalServiceActivities.java。
Using a Messenger
Compared to AIDL
When you need to perform IPC, using aMessenger
for your interface is simpler than implementing it with AIDL, because Messenger
queues all calls to the service, whereas, a pure AIDL interface sends simultaneous requests to the service, which must then handle multi-threading.
For most applications, the service doesn't need to perform multi-threading, so using a Messenger
allows the service to handle one call at a time. If it's important that your service be multi-threaded, then you should use AIDL to define your interface.
如果service需要与远程的进程通讯,可以用Messenger为service提供一个接口。这种技术可以处理进程间的通讯。
下面是使用messenger的简介:
- service 要实现Handler,它可以收到每一个client调用的回调。
Handler用于创建Messenger对象。
(which is a reference to theHandler
).- Messenger创建一个IBinder,client调用onBind()时,IBinder要返回给client的
- Clients 通过IBinder实例化Messenger(that references the service's
Handler
),Messenger为了client发送Message对象到service。 - service 通过Handler收到每一个Message, 在
handleMessage()
方法明确的处理.
利用这种方式,service中没有方法可一个被Client调用。client通过传递消息(messages)到service的Handler中。
下面是一个简单的例子,通过Messenger的方式实现:
publicclassMessengerServiceextendsService{ /** Command to the service to display a message */ staticfinalint MSG_SAY_HELLO =1; /** * Handler of incoming messages from clients. */ classIncomingHandlerextendsHandler{ @Override publicvoid 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. */ finalMessenger mMessenger =newMessenger(newIncomingHandler()); /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override publicIBinder onBind(Intent intent){ Toast.makeText(getApplicationContext(),"binding",Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); }}
主意:Handler 的handleMessage()方法收到Message,并且根据Message的what变量 确定需要做什么。
Client的需要实现的是,基于返回的IBinder创建Messenger对象,并且通过send()发送Message给service的Handler。例如,下面的activity绑定了service,而且传递MSG_SAY_HELLO
message到service中处理:
publicclassActivityMessengerextendsActivity{ /** 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. */ privateServiceConnection mConnection =newServiceConnection(){ publicvoid 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 =newMessenger(service); mBound =true; } publicvoid 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; } }; publicvoid 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 protectedvoid onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protectedvoid onStart(){ super.onStart(); // Bind to the service bindService(newIntent(this,MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } @Override protectedvoid onStop(){ super.onStop(); // Unbind from the service if(mBound){ unbindService(mConnection); mBound =false; } }}
注意: 这个例子没有体现service是如何响应client。如果想service响应client,需要在client里面创建一个Messenger。当client收到onServiceConnected()回调,就会发送Message到service,send()方法的Message变量replyTo包括客户端的Messenger.
可以从MessengerService.java
(service) 和MessengerServiceActivities.java
(client) 例子中知道如何提供双向通讯.
Binding to a Service
应用的组件调用bindService()可以绑定service。系统会调用service的onBind()方法,返回一个IBinder,以便组件能与service很好的交互。
绑定过程是异步。bindService()能很快的返回,并且不会返回IBinder到客户端。client收到IBinder时,会创建ServiceConnection
并且通过它绑定到service。ServiceConnection
包括一个系统调用传递IBinder的方法。
Note: 仅仅activities, services, 和content providers 能够与service绑定-不能把broadcast receiver与service绑定。
因此,为了让client与service绑定,必须:
- 实现
ServiceConnection
.实现必须重写两个方法:
onServiceConnected()
- The system calls this to deliver the
IBinder
returned by the service'sonBind()
method. onServiceDisconnected()
- The Android system calls this when the connection to the service is unexpectedly lost, such as when the service has crashed or has been killed. This is not called when the client unbinds.
- 调用
bindService()
, 传递ServiceConnection
的实现. - 当系统调用
onServiceConnected()
方法, 就可以调用service的方法,能调用的方法必须是定义在接口中的。 - 调用
unbindService()取消与service的连接
.当client销毁时,就会取消与service的绑定,但是当做完任务或者activity停止的时候,要取消绑定,以便服务在不需要的时候能够停止
例如,下面的一段代码通过继承Binder类,从而client和service可以绑定,因此,需要将返回的IBinder
转换成LocalService,并取得LocalService对象:
LocalService mService;privateServiceConnection mConnection =newServiceConnection(){ // Called when the connection with the service is established publicvoid 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 publicvoid onServiceDisconnected(ComponentName className){ Log.e(TAG,"onServiceDisconnected"); mBound =false; }};
利用这个 ServiceConnection
, client可以通过bindService()绑定service. 例如:
Intent intent =newIntent(this,LocalService.class); bindService(intent, mConnection,Context.BIND_AUTO_CREATE);
- 第一个变量是通过Intent指定要绑定的service.
- 第二个参数是
ServiceConnection
对象. - 第三个参数是设置绑定的选项。通常是
BIND_AUTO_CREATE,当service不在运行时,会自动创建。其他值有
BIND_DEBUG_UNBIND
和BIND_NOT_FOREGROUND
, 或者是0(没有任何意义).
补充的注意:
下面是在绑定服务的时候需要非常注意的内容:
- 总是需要捕捉
DeadObjectException
异常, 当连接断开的时候回抛出来。这是唯一通过远程方法抛出的异常。 - 对象通过进程引用计数。
- 绑定和不绑定要和开始,关闭方法配对。例如:
- 当activity可见的时候,要和service交互,需要在onStart()方法中绑定,在onStop()方法中取消绑定。
- 当activity需要收到回复,甚至当它已经在后台停止,这时在onCreate()绑定,在onDestroy()取消绑定。这意味着你的活动在整个运行时间需要使用服务(即使它在后台运行),因此,如果service在另一个进程中,然后你增加service的权重,系统将会优先杀死它。
Note: 通常不能在activity的
onResume()
和onPause(),方法中绑定和取消绑定
, 这个回调函数发生在每一个生命周期过度中,应该保证这种转变发生越少越好。同样的, 如果在应用中多个activity绑定同一个service,并且在两个activity中有过度,那么service会在前一个activity的暂停的时候取消绑定,在另外一个activity上重新绑定。 (activity生命周期的协调在Activities 文档中)
更多的例子,在 ApiDemos的RemoteService.java可以看到如何绑定service。
Managing the Lifecycle of a Bound Service
当service没有被任何的client绑定,android系统会杀死它(除非中途被其他的组件调用了onStartCommand())。按照这种原理, 绑定service是不需要管理它的生命周期的-android系统会根据绑定原则自动管理service。
然而,如果选择onStartCommand()方法实现,就必须明确的停止service,因为服务当前状态被认为是started。在这种案例下,service一直运行直到调用stopSelf()或者其他的组件调用stopService()方法,不管他绑定任何clients.这里的意思是说:当一个绑定service通过onStartCommand()启动,需要stopSelf()和stopService()方法来停止它,即使是中间绑定其他的客户端。
补充点,如果service已经启动并且接受了client的绑定,那么当系统调用onUnbind()方法时,如果想client下次绑定service的时候调用onRebind(),可以选择返回true,(而不是重新调用onBind()
). onRebind()
返回 void, 但是client在onServiceConnected()还是接受IBinder. 下面的图阐明了上面的逻辑。