Android Service进阶教程

 

概述绑定式Service在CS结构中扮演着Server的角色。绑定式Service允许其他组件(如Activ...



概述

绑定式Service在CS结构中扮演着Server的角色。绑定式Service允许其他组件(如Activity)绑定该Service、发送请求、接收响应、甚至IPC通信( interprocess communication)。绑定式Service通常服务于其他应用程序的组件、且并不总在后台运行(does not run in the background indefinitely)。

如需访问bound Service的官方原文,您可以点击以下链接:

https://developer.android.com/guide/components/bound-services.html

绑定式Service基础

绑定式Service是一个继承于Service的类。它可以与其他应用交互。为了实现绑定Service,您必须重写 onBind() 方法。该方法返回一个 IBinder 接口,此接口是绑定式Service与其它应用组件交互的桥梁。

其它应用组件可调用 bindService() 方法绑定Service。该方法需要传入的参数中包含一个实现了 ServiceConnection 接口的对象。该对象监控着组件与Service的绑定状态(which monitors the connection with the service)。

bindService() 方法并不返回数据,而一旦系统创建了组件与Service的连接,ServiceConnection 接口中的方法 onServiceConnected() 将被回调,此时实现了IBinder接口的对象将传递至组件中,这样便实现了Service与绑定组件的通信(to deliver the IBinder that the client can use to communicate with the service)。

Service可同时与多个组件绑定。然而Service仅在绑定的第一个组件时回调 onBind() 方法以获得 IBinder 接口对象,之后与该Service绑定的组件都传递的是同一个 IBinder 接口对象,而且并不再回调 onBind() 方法。

当Service与绑定它的最后一个组件解绑时,系统将该Service 销毁(destroy),当然若Service还使用start方式启动过(调用 startService() 方法启动),则该Service并不会destroy。

创建bound Service时,最重要的就是实现 onBind() 回调方法中的返回接口 IBinder,下面将介绍几种不同实现 IBinder 接口的方式。

创建绑定式Service

以下列举了三种实现 IBinder 接口的方式:

1. 继承 Binder类(Extending the Binder class)

Binder 是一个实现了 IBinder 接口的类。若Service只允许被本应用所在的进程访问(这是大多数情况),您需要继承 Binder 类,并将该对象作为 onBind() 方法的返回值。这样,与Service绑定的组件就可以通过该返回对象访问 Binder 的继承类中的public方法、甚至是Service中的方法(to directly access public methods available in either the Binder implementation or even the Service)。

若在您的应用程序中,Service仅作为一个在后台工作的组件,那么这种方式最好不过了。除非您需要Service进行跨进程通信。

2. 使用 Messenger(Using a Messenger)

如需要使用 IBinder 进行跨进程通信,您应当为Service创建一个 Messenger 对象。这样,Service可以定义一个 Handler 对象以接受不同类型的 MessageHandlerMessenger 的基础,它可以在客户端与IBinder 共享(This Handler is the basis for a Messenger that can then share an IBinder with the client),并允许使用Message 对象向Service端发送指令(allowing the client to send commands to the service using Message objects)。除此之外,亦可以在client端定义Messenger,这样Service端可以回传信息。

3. 使用 AIDL(Using AIDL)

AIDL(Android Interface Definition Language)是Android接口定义语言的缩写。大多数应用程序并不应该使用AIDL创建bound Service,因为这需要应用程序有处理多线程的能力,而这会使应用程序变得复杂,在本文中并不打算具体介绍AIDL,如需访问有关AIDL的官方原文,您可以访问以下链接:

https://developer.android.com/guide/components/aidl.html

继承Binder类

若您的Service仅是应用程序内部使用,并不需要跨进程通信,那么可以继承Binder类。这样,与Service绑定的组件可以直接访问Service中的public方法。

!请注意:这种继承Binder类的方式仅适用于Service与绑定的组件处于同一应用程序或进程的情况,当然这也是最普遍的情况。举例来说,在播放音乐应用程序中,可以使用这种方式将一个Activity与Service绑定,而Service用于在后台播放音乐。

创建方式:

  1. 在Service类中创建一个继承于 Binder 的内部类。在Service类中定义public方法,以便client端可以访问。在继承于Binder 的内部类中返回该Service实例。将该内部类实例作为onBind() 返回参数。
  2. 在客户端中的onServiceConnected() 回调方法中接受 Binder 对象,并访问Service中的public方法。
示例如下:

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 实例。这样,client端可以通过该实例访问Service中pubic方法。比如,client端可以访问 LocalService 中的public方法 getRandomNumber() ,如下所示:
首先,在Activity的 onStart() 回调方法中调用 bindService() 绑定 LocalService,这时, LocalService 中的 onCreate() 与 onBind() 依次回调;接着, ServiceConnection  中的 onServiceConnected() 方法回调,表示组件与Service已绑定,这时可以通过回传给 onServiceConnected() 中的 IBinder  接口对象获得 LocalService 实例,一旦获得了该实例,便可以调用 LocalService 中的public方法,如 getRandomNumber() 方法。

!请注意:Service应在合适的时候与组件解除绑定,本例中应在 onStop() 中解除与Service的绑定。

使用Messenger绑定Service

当Service需要进行IPC通信时,应在Service中使用 Messenger。使用 Messenger 的方式如下:

  • 继承 Handler 类,并实现回调方法 handleMessage(),每当client端访问Service中的方法时,handleMessage() 都将回调(receives a callback for each call from a client)。
  • 需在Service中创建一个 Messenger 对象,构造该对象需传入一个 Handler 参数。
  • 调用MessengergetBinder() 返回一个IBinder对象,将该对象作为onBind() 回调方法的返回值。
  • client端通过onServiceConnected() 回传的IBinder参数,构造Messenger 对象,并将Message 信息传入Messenger 对象,发送给Service。
  • Service在 Handler 的 handleMessage() 方法中接收 Message 信息。
按照如此方式,client端并没有显式调用Service中的方法,而是传递了 Message 对象,并在Service的Handler中接收。

以下是Service端示例:

以下是client端接收示例:

本例中并未包含Service端向client端发送消息的逻辑,如需要Service答复client发送的消息,需在client端也创建一个 Messenger 对象,当 onServiceConnected() 方法被回调时,在 send() 方法中传入 replyTo 参数。

绑定一个Service

绑定Service是一个异步过程(The binding is asynchronous):应用程序中的组件调用 bindService() 绑定一个Service,bindService() 立即返回;接着系统回调Service的 onBind() 方法,而client并不会接收到 IBinder 参数,为了接收该参数,需要创建一个 ServiceConnection 实例,并将该实例传入 bindService() 中,系统会将 IBinder 回传至 ServiceConnection 的回调方法中(The ServiceConnection includes a callback method that the system calls to deliver the IBinder)。

!请注意:只有activities、services、content providers可以绑定Service, broadcast receiver不能绑定Service(you cannot bind to a service from a broadcast receiver)。

所以,绑定Service应按如下步骤:

1. 实现 ServiceConnection 接口:

  • 实现 onServiceConnected() 方法:当client与Service建立绑定时,系统回调该方法,并将 onBind() 返回的 IBinder 参数回传至该方法中;
  •         实现 onServiceDisconnected() 方法:当绑定的Service意外终止时( unexpectedly lost),系统回调该方法,如Service被进程kill或Service崩溃(crashed)。系统若回调 unBindService() 方法,将不会回调 onServiceDisconnected() 方法。
2. 调用 bindService() ,并传入 ServiceConnection 的实现类对象;

3. 当系统回调 onServiceConnected() 时,表示client与Service已绑定,此时可以访问Service中的public方法。

4. 系统回调 unbindService() ,解除绑定。

  • !请注意:当client与Service绑定时,系统destroy掉client端,这将破坏绑定状态(destruction causes the client to unbind)。好的做法是只要client与Service交互完成,就解除绑定(to unbind the client as soon as it is done interacting with the service)。
下面的代码片段演示了如何绑定Service:

下面演示了启动绑定的方式:

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);其中第三个参数表示绑定的模式,通常为BIND_AUTO_CREATE,表示当Service还尚未处于alive状态时创建该Service。其它可用的参数为BIND_DEBUG_UNBIND、BIND_NOT_FOREGROUND,若不打算指定模式,可传入0。

需要额外注意的地方

1. 当连接错误时,系统会抛出DeadObjectException异常,这也是在client端调用Service中的方法时可能抛出的唯一异常(This is the only exception thrown by remote methods)。

2. binding unbinding应成对出调用。

  • 若当Activity在前台处于运行状态时,需要与绑定的Service交互,那么应在 onStart() 方法中 bindService(),在 onStop() 中 unbindService()
  • 若当Activity在后台处于stop状态时,那么应在 onCreate() 方法中 bindService() ,在 onDestroy() 中 unbindService()。此时系统将更易kill该Service。
!请注意:请不要在 onResume()onPause() 方法中绑定、解绑Service,因为这两个生命周期回调方法经常被回调,频繁的绑定与解绑会降低程序的执行效率。

管理Bound Service的生命周期

当Service不再与任何Client绑定时,系统将回收该Service(除非Service也用Start方式启动了(将回调 onStartCommand() 方法)),您无需手动管理一个纯bound Service的生命周期(you don't have to manage the lifecycle of your service if it's purely a bound service),系统会自动管理。

无论Service绑定了多少个client,若您还回调了 onStartCommand() 方法,那么必须显式stop该Service,可以通过在Service中调用 stopSelf() 方法、或在其他组件中调用 stopService() stop该Service。

若通过两种方式(start、bound)同时启动了一个Service,那么如果希望Service在下一次绑定该client时回调 onRebind() 方法,应在 onUnbind() 方法中返回true。按照这种方式,再次与该Service绑定的client仍可以在 onServiceConnected() 方法中接收到回传的 IBinder 参数。如下图所示:


    关注 android时空


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册