绑定服务

绑定服务是客户端-服务器接口中的服务器。绑定服务可让组件(例如 Activity)绑定到服务、发送请求、接收响应,甚至执行进程间通信 (IPC)。 绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行。

本文向您介绍如何创建绑定服务,包括如何绑定到来自其他应用组件的服务。 不过,您还应参阅服务文档,了解有关一般服务的更多信息,例如:如何利用服务传送通知、如何将服务设置为在前台运行等等。

基础知识

绑定服务是 Service 类的实现,可让其他应用与其绑定和交互。要提供服务绑定,您必须实现 onBind()回调方法。该方法返回的 IBinder 对象定义了客户端用来与服务进行交互的编程接口。

绑定到已启动服务

正如服务文档中所述,您可以创建同时具有已启动和绑定两种状态的服务。 也就是说,可通过调用startService() 启动该服务,让服务无限期运行;此外,还可通过调用 bindService() 使客户端绑定到服务。

如果您确实允许服务同时具有已启动和绑定状态,则服务启动后,系统“不会”在所有客户端都取消绑定时销毁服务。 为此,您必须通过调用 stopSelf()stopService() 显式停止服务。

尽管您通常应该实现 onBind() onStartCommand(),但有时需要同时实现这两者。例如,音乐播放器可能发现让其服务无限期运行并同时提供绑定很有用处。 这样一来,Activity 便可启动服务进行音乐播放,即使用户离开应用,音乐播放也不会停止。 然后,当用户返回应用时,Activity 可绑定到服务,重新获得回放控制权。

请务必阅读管理绑定服务的生命周期部分,详细了解有关为已启动服务添加绑定时该服务的生命周期信息。

客户端可通过调用 bindService() 绑定到服务。调用时,它必须提供 ServiceConnection 的实现,后者会监控与服务的连接。bindService() 方法会立即无值返回,但当 Android 系统创建客户端与服务之间的连接时,会对 ServiceConnection 调用 onServiceConnected(),向客户端传递用来与服务通信的 IBinder

多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。

当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。

当您实现绑定服务时,最重要的环节是定义您的 onBind() 回调方法返回的接口。您可以通过几种不同的方法定义服务的 IBinder 接口,下文对这些方法逐一做了阐述。

创建绑定服务

创建提供绑定的服务时,您必须提供 IBinder,用以提供客户端用来与服务进行交互的编程接口。 您可以通过三种方法定义接口:

:大多数应用“都不会”使用 AIDL 来创建绑定服务,因为它可能要求具备多线程处理能力,并可能导致实现的复杂性增加。因此,AIDL 并不适合大多数应用,本文也不会阐述如何将其用于您的服务。如果您确定自己需要直接使用 AIDL,请参阅 AIDL 文档。

扩展 Binder 类

如果您的服务仅供本地应用使用,不需要跨进程工作,则可以实现自有 Binder 类,让您的客户端通过该类直接访问服务中的公共方法。

:此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效。 例如,对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方法非常有效。

以下是具体的设置方法:

  1. 在您的服务中,创建一个可满足下列任一要求的

    实例:

    • 包含客户端可调用的公共方法
    • 返回当前 Service 实例,其中包含客户端可调用的公共方法
    • 或返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法
  2. onBind() 回调方法返回此 Binder 实例。

  3. 在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务。

:之所以要求服务和客户端必须在同一应用内,是为了便于客户端转换返回的对象和正确调用其 API。服务和客户端还必须在同一进程内,因为此方法不执行任何跨进程编组。

例如,以下这个服务可让客户端通过 Binder 实现访问服务中的方法:

 

LocalBinder 为客户端提供 getService() 方法,以检索 LocalService 的当前实例。这样,客户端便可调用服务中的公共方法。 例如,客户端可调用服务中的 getRandomNumber()

点击按钮时,以下这个 Activity 会绑定到 LocalService 并调用 getRandomNumber()

 

上例说明了客户端如何使用 ServiceConnection 的实现和 onServiceConnected() 回调绑定到服务。下文更详细介绍了绑定到服务的过程。

:在上例中,onStop() 方法将客户端与服务取消绑定。 客户端应在适当时机与服务取消绑定,如附加说明中所述。

如需查看更多示例代码,请参见 ApiDemos 中的 LocalService.java 类和 LocalServiceActivities.java 类。

使用 Messenger

与 AIDL 比较

当您需要执行 IPC 时,为您的接口使用 Messenger 要比使用 AIDL 实现它更加简单,因为 Messenger 会将所有服务调用排入队列,而纯粹的 AIDL 接口会同时向服务发送多个请求,服务随后必须应对多线程处理。

对于大多数应用,服务不需要执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果您的服务必须执行多线程处理,则应使用 AIDL 来定义接口。

如需让服务与远程进程通信,则可使用 Messenger 为您的服务提供接口。利用此方法,您无需使用 AIDL 便可执行进程间通信 (IPC)。

以下是 Messenger 的使用方法摘要:

这样,客户端并没有调用服务的“方法”。而客户端传递的“消息”(Message 对象)是服务在其 Handler 中接收的。

以下是一个使用 Messenger 接口的简单服务示例:

 

请注意,服务就是在 HandlerhandleMessage() 方法中接收传入的 Message,并根据 what 成员决定下一步操作。

客户端只需根据服务返回的 IBinder 创建一个 Messenger,然后利用 send() 发送一条消息。例如,以下就是一个绑定到服务并向服务传递 MSG_SAY_HELLO 消息的简单 Activity:

 

请注意,此示例并未说明服务如何对客户端作出响应。如果您想让服务作出响应,则还需要在客户端中创建一个 Messenger。然后,当客户端收到 onServiceConnected() 回调时,会向服务发送一条 Message,并在其 send() 方法的 replyTo 参数中包含客户端的 Messenger

如需查看如何提供双向消息传递的示例,请参阅 MessengerService.java(服务)和 MessengerServiceActivities.java(客户端)示例。

绑定到服务

应用组件(客户端)可通过调用 bindService() 绑定到服务。Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder

绑定是异步的。bindService() 会立即返回,“不会”使 IBinder 返回客户端。要接收 IBinder,客户端必须创建一个 ServiceConnection 实例,并将其传递给 bindService()ServiceConnection 包括一个回调方法,系统通过调用它来传递 IBinder

:只有 Activity、服务和内容提供程序可以绑定到服务 — 您无法从广播接收器绑定到服务。

因此,要想从您的客户端绑定到服务,您必须:

  1. 实现

    您的实现必须重写两个回调方法:

    • onServiceConnected()

      系统会调用该方法以传递服务的 onBind() 方法返回的 IBinder

    • onServiceDisconnected()

      Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。当客户端取消绑定时,系统“不会”调用该方法。

  2. 调用 bindService(),传递 ServiceConnection 实现。

  3. 当系统调用您的 onServiceConnected() 回调方法时,您可以使用接口定义的方法开始调用服务。

  4. 要断开与服务的连接,请调用

    如果应用在客户端仍绑定到服务时销毁客户端,则销毁会导致客户端取消绑定。 更好的做法是在客户端与服务交互完成后立即取消绑定客户端。 这样可以关闭空闲服务。如需了解有关绑定和取消绑定的适当时机的详细信息,请参阅附加说明

例如,以下代码段通过扩展 Binder 类将客户端与上面创建的服务相连,因此它只需将返回的 IBinder 转换为 LocalService 类并请求 LocalService 实例:

 

客户端可通过将此 ServiceConnection 传递至 bindService() 绑定到服务。例如:

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

 

附加说明

以下是一些有关绑定到服务的重要说明:

如需查看更多显示如何绑定到服务的示例代码,请参阅 ApiDemos 中的 RemoteService.java 类。

管理绑定服务的生命周期

当服务与所有客户端之间的绑定全部取消时,Android 系统便会销毁服务(除非还使用 onStartCommand()启动了该服务)。因此,如果您的服务是纯粹的绑定服务,则无需对其生命周期进行管理 — Android 系统会根据它是否绑定到任何客户端代您管理。

不过,如果您选择实现 onStartCommand() 回调方法,则您必须显式停止服务,因为系统现在已将服务视为已启动。在此情况下,服务将一直运行到其通过 stopSelf() 自行停止,或其他组件调用 stopService()为止,无论其是否绑定到任何客户端。

此外,如果您的服务已启动并接受绑定,则当系统调用您的 onUnbind() 方法时,如果您想在客户端下一次绑定到服务时接收 onRebind() 调用,则可选择返回 trueonRebind() 返回空值,但客户端仍在其onServiceConnected() 回调中接收 IBinder。下文图 1 说明了这种生命周期的逻辑。

img

图 1. 允许绑定的已启动服务的生命周期。

如需了解有关已启动服务生命周期的详细信息,请参阅服务文档。

 

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