绑定服务是客户端-服务器界面中的服务器。它允许活动等组件绑定到该服务,发送请求,接收响应,并执行进程间通信 (IPC)。绑定服务通常只在其服务于另一个应用组件时才存在,并且不会在后台无限期运行。
本文档介绍了如何创建绑定服务,包括如何从其他应用组件绑定到该服务。有关服务的更多通用信息,例如如何从服务传递通知以及将服务设置为在前台运行,请参阅服务概览。
基础知识
绑定服务是 Service
类的一个实现,它允许其他应用绑定到它并与其交互。要为服务提供绑定,您需要实现 onBind()
回调方法。此方法返回一个 IBinder
对象,该对象定义了客户端可用于与服务交互的编程接口。
绑定到已启动的服务
如服务概览中所述,您可以创建既已启动又已绑定的服务。也就是说,您可以通过调用 startService()
来启动服务,这会让服务无限期运行。您还可以通过调用 bindService()
让客户端绑定到该服务。
如果您允许您的服务既已启动又已绑定,那么当服务启动时,系统在所有客户端解除绑定时不会销毁服务。相反,您必须通过调用 stopSelf()
或 stopService()
来显式停止服务。
尽管您通常只实现 onBind()
或 onStartCommand()
,但有时两者都需要实现。例如,音乐播放器可能会发现让其服务无限期运行并提供绑定功能很有用。这样,活动可以启动服务来播放音乐,即使用户离开应用,音乐也会继续播放。然后,当用户返回应用时,活动可以绑定到服务以重新获得播放控制权。
有关在已启动服务中添加绑定时的服务生命周期的更多信息,请参阅管理绑定服务的生命周期部分。
客户端通过调用 bindService()
绑定到服务。绑定时,它必须提供一个 ServiceConnection
的实现,该实现用于监视与服务的连接。bindService()
的返回值表示请求的服务是否存在以及客户端是否被允许访问它。
当 Android 系统在客户端和服务之间创建连接时,它会在 ServiceConnection
上调用 onServiceConnected()
。onServiceConnected()
方法包含一个 IBinder
参数,客户端随后使用该参数与绑定服务进行通信。
您可以同时连接多个客户端到一个服务。但是,系统会缓存 IBinder
服务通信通道。换句话说,系统仅在第一个客户端绑定时才调用服务的 onBind()
方法来生成 IBinder
。然后,系统会将相同的 IBinder
传递给所有绑定到同一服务的其他客户端,而无需再次调用 onBind()
。
当最后一个客户端与服务解除绑定时,系统会销毁服务,除非该服务是使用 startService()
启动的。
绑定服务实现中最重要的部分是定义 onBind()
回调方法返回的接口。以下部分讨论了定义服务 IBinder
接口的几种方法。
创建绑定服务
创建提供绑定的服务时,您必须提供一个 IBinder
,它提供客户端可用于与服务交互的编程接口。有三种方法可以定义该接口:
- 扩展 Binder 类
- 如果您的服务是您自己应用私有的,并且与客户端运行在同一进程中(这很常见),则可以通过扩展
Binder
类并从onBind()
返回其实例来创建接口。客户端会收到此Binder
,并可以使用它直接访问Binder
实现或Service
中可用的公共方法。当您的服务仅仅是自己应用的后台工作器时,这是首选技术。唯一不推荐使用这种方式创建接口的情况是,如果您的服务被其他应用或跨独立进程使用。
- 使用 Messenger
- 如果您需要您的接口跨不同进程工作,您可以使用
Messenger
为服务创建接口。通过这种方式,服务定义了一个Handler
来响应不同类型的Message
对象。这个
Handler
是Messenger
的基础,后者可以与客户端共享一个IBinder
,让客户端使用Message
对象向服务发送命令。此外,客户端也可以定义自己的Messenger
,以便服务可以发送消息回来。这是执行进程间通信 (IPC) 的最简单方法,因为
Messenger
将所有请求排队到单个线程中,这样您就不必将服务设计为线程安全。 - 使用 AIDL
- Android 接口定义语言 (AIDL) 将对象分解为操作系统可以理解的基元,并将其跨进程进行封送以执行 IPC。之前的技术,即使用
Messenger
,实际上是以 AIDL 作为其底层结构。如前一节所述,
Messenger
在单个线程中创建所有客户端请求的队列,因此服务一次接收一个请求。但是,如果您希望服务同时处理多个请求,则可以直接使用 AIDL。在这种情况下,您的服务必须是线程安全的并且能够进行多线程处理。要直接使用 AIDL,请创建一个定义编程接口的
.aidl
文件。Android SDK 工具使用此文件生成一个抽象类,该类实现接口并处理 IPC,然后您可以在服务中扩展该类。
注意:对于大多数应用来说,AIDL 并不是创建绑定服务的最佳选择,因为它可能需要多线程能力,并可能导致更复杂的实现。因此,本文档不讨论如何在您的服务中使用它。如果您确定需要直接使用 AIDL,请参阅 AIDL 文档。
扩展 Binder 类
如果只有本地应用使用您的服务,并且不需要跨进程工作,那么您可以实现自己的 Binder
类,为客户端提供对服务中公共方法的直接访问。
注意:这仅在客户端和服务位于同一应用和进程中时有效,这是最常见的情况。例如,这对于需要将其活动绑定到自身后台播放音乐服务的音乐应用来说非常适用。
设置方法如下:
- 在您的服务中,创建一个
Binder
实例,它执行以下操作之一:- 包含客户端可以调用的公共方法。
- 返回当前的
Service
实例,该实例具有客户端可以调用的公共方法。 - 返回服务托管的另一个类的实例,该实例具有客户端可以调用的公共方法。
- 从
onBind()
回调方法中返回此Binder
实例。 - 在客户端中,从
onServiceConnected()
回调方法接收Binder
,然后使用提供的方法调用绑定服务。
注意:服务和客户端必须位于同一应用中,以便客户端可以强制转换返回的对象并正确调用其 API。服务和客户端也必须位于同一进程中,因为此技术不执行任何跨进程封送。
例如,这是一个通过 Binder
实现为客户端提供服务中方法访问权限的服务:
Kotlin
class LocalService : Service() { // Binder given to clients. private val binder = LocalBinder() // Random number generator. private val mGenerator = Random() /** Method for clients. */ val randomNumber: Int get() = mGenerator.nextInt(100) /** * 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. */ inner class LocalBinder : Binder() { // Return this instance of LocalService so clients can call public methods. fun getService(): LocalService = this@LocalService } override fun onBind(intent: Intent): IBinder { return binder } }
Java
public class LocalService extends Service { // Binder given to clients. private final IBinder binder = 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 binder; } /** Method for clients. */ public int getRandomNumber() { return mGenerator.nextInt(100); } }
LocalBinder
提供 getService()
方法,供客户端检索 LocalService
的当前实例。这使得客户端可以调用服务中的公共方法。例如,客户端可以从服务中调用 getRandomNumber()
。
这是一个绑定到 LocalService
并在点击按钮时调用 getRandomNumber()
的活动:
Kotlin
class BindingActivity : Activity() { private lateinit var mService: LocalService private var mBound: Boolean = false /** Defines callbacks for service binding, passed to bindService(). */ private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // We've bound to LocalService, cast the IBinder and get LocalService instance. val binder = service as LocalService.LocalBinder mService = binder.getService() mBound = true } override fun onServiceDisconnected(arg0: ComponentName) { mBound = false } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) } override fun onStart() { super.onStart() // Bind to LocalService. Intent(this, LocalService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) } } override fun onStop() { super.onStop() unbindService(connection) mBound = false } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute). */ fun onButtonClick(v: View) { if (mBound) { // Call a method from the LocalService. // However, if this call is something that might hang, then put this request // in a separate thread to avoid slowing down the activity performance. val num: Int = mService.randomNumber Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show() } } }
Java
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, connection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); unbindService(connection); 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 is something that might hang, then put this request // 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 connection = 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()
回调绑定到服务。下一节将提供有关此绑定服务过程的更多信息。
注意:在上述示例中,onStop()
方法将客户端与服务解除绑定。请在适当的时间解除客户端与服务的绑定,如其他注意事项部分所述。
有关更多示例代码,请参阅 LocalService.java
类和 LocalServiceActivities.java
类,这些类位于 ApiDemos 中。
使用 Messenger
如果您的服务需要与远程进程通信,那么您可以使用 Messenger
为您的服务提供接口。此技术允许您执行进程间通信 (IPC),而无需使用 AIDL。
使用 Messenger
作为您的接口比使用 AIDL 更简单,因为 Messenger
会将所有对服务的调用排队。纯 AIDL 接口会向服务同时发送请求,然后服务必须处理多线程。
对于大多数应用程序来说,服务不需要执行多线程,因此使用 Messenger
可以让服务一次处理一个调用。如果您的服务必须是多线程的,请使用 AIDL 来定义您的接口。
以下是使用 Messenger
的摘要:
- 服务实现了一个
Handler
,用于接收来自客户端的每次调用的回调。 - 服务使用
Handler
创建一个Messenger
对象(它引用了Handler
)。 Messenger
创建一个IBinder
,服务通过onBind()
将其返回给客户端。- 客户端使用
IBinder
实例化Messenger
(它引用了服务的Handler
),客户端使用它向服务发送Message
对象。 - 服务在其
Handler
中接收每个Message
——具体来说,是在handleMessage()
方法中。
这样,客户端就无法在服务上调用方法。相反,客户端会传递消息(Message
对象),服务在其 Handler
中接收这些消息。
这是一个使用 Messenger
接口的简单服务示例:
Kotlin
/** Command to the service to display a message. */ private const val MSG_SAY_HELLO = 1 class MessengerService : Service() { /** * Target we publish for clients to send messages to IncomingHandler. */ private lateinit var mMessenger: Messenger /** * Handler of incoming messages from clients. */ internal class IncomingHandler( context: Context, private val applicationContext: Context = context.applicationContext ) : Handler() { override fun handleMessage(msg: Message) { when (msg.what) { MSG_SAY_HELLO -> Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show() else -> super.handleMessage(msg) } } } /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ override fun onBind(intent: Intent): IBinder? { Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show() mMessenger = Messenger(IncomingHandler(this)) return mMessenger.binder } }
Java
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. */ static class IncomingHandler extends Handler { private Context applicationContext; IncomingHandler(Context context) { applicationContext = context.getApplicationContext(); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ Messenger mMessenger; /** * 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(); mMessenger = new Messenger(new IncomingHandler(this)); return mMessenger.getBinder(); } }
Handler
中的 handleMessage()
方法是服务接收传入的 Message
并根据 what
成员决定执行操作的地方。
客户端需要做的就是根据服务返回的 IBinder
创建一个 Messenger
,然后使用 send()
发送消息。例如,这是一个绑定到服务并向服务发送 MSG_SAY_HELLO
消息的活动:
Kotlin
class ActivityMessenger : Activity() { /** Messenger for communicating with the service. */ private var mService: Messenger? = null /** Flag indicating whether we have called bind on the service. */ private var bound: Boolean = false /** * Class for interacting with the main interface of the service. */ private val mConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // 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 = Messenger(service) bound = true } override fun onServiceDisconnected(className: ComponentName) { // This is called when the connection with the service has been // unexpectedly disconnected—that is, its process crashed. mService = null bound = false } } fun sayHello(v: View) { if (!bound) return // Create and send a message to the service, using a supported 'what' value. val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0) try { mService?.send(msg) } catch (e: RemoteException) { e.printStackTrace() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) } override fun onStart() { super.onStart() // Bind to the service. Intent(this, MessengerService::class.java).also { intent -> bindService(intent, mConnection, Context.BIND_AUTO_CREATE) } } override fun onStop() { super.onStop() // Unbind from the service. if (bound) { unbindService(mConnection) bound = false } } }
Java
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 bound; /** * 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); bound = 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; bound = false; } }; public void sayHello(View v) { if (!bound) 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 (bound) { unbindService(mConnection); bound = false; } } }
此示例未展示服务如何响应客户端。如果您希望服务响应,您还需要在客户端中创建一个 Messenger
。当客户端收到 onServiceConnected()
回调时,它会向服务发送一个 Message
,其中包含客户端的 Messenger
在 send()
方法的 replyTo
参数中。
您可以在 MessengerService.java
(服务)和 MessengerServiceActivities.java
(客户端)示例中查看如何提供双向消息的示例。
绑定到服务
应用组件(客户端)可以通过调用 bindService()
绑定到服务。Android 系统随后调用服务的 onBind()
方法,该方法返回一个用于与服务交互的 IBinder
。
绑定是异步的,bindService()
会立即返回,但不会将 IBinder
返回给客户端。要接收 IBinder
,客户端必须创建 ServiceConnection
的实例并将其传递给 bindService()
。ServiceConnection
包含一个回调方法,系统会调用该方法来传递 IBinder
。
注意:只有活动、服务和内容提供程序可以绑定到服务——您不能从广播接收器绑定到服务。
要从客户端绑定到服务,请按照以下步骤操作:
- 实现
ServiceConnection
。您的实现必须重写两个回调方法:
onServiceConnected()
- 系统调用此方法来传递服务
onBind()
方法返回的IBinder
。 onServiceDisconnected()
- 当与服务的连接意外丢失时,例如服务崩溃或被终止时,Android 系统会调用此方法。当客户端解除绑定时,不会调用此方法。
- 调用
bindService()
,并传入ServiceConnection
实现。注意:如果方法返回 false,则您的客户端与服务没有有效连接。但是,请务必在客户端中调用
unbindService()
。否则,您的客户端将阻止服务在空闲时关闭。 - 当系统调用您的
onServiceConnected()
回调方法时,您可以使用接口定义的方法开始调用服务。 - 要断开与服务的连接,请调用
unbindService()
。如果您的应用在销毁客户端时,客户端仍绑定到服务,则销毁会导致客户端解除绑定。更好的做法是,一旦客户端完成与服务的交互,就立即解除绑定。这样做可以让空闲服务关闭。有关何时绑定和解除绑定的更多信息,请参阅其他注意事项部分。
以下示例将客户端连接到之前通过扩展 Binder 类创建的服务,因此它所需要做的就是将返回的 IBinder
强制转换为 LocalBinder
类并请求 LocalService
实例:
Kotlin
var mService: LocalService val mConnection = object : ServiceConnection { // Called when the connection with the service is established. override fun onServiceConnected(className: ComponentName, service: IBinder) { // 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. val binder = service as LocalService.LocalBinder mService = binder.getService() mBound = true } // Called when the connection with the service disconnects unexpectedly. override fun onServiceDisconnected(className: ComponentName) { Log.e(TAG, "onServiceDisconnected") mBound = false } }
Java
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()
来绑定到服务,如以下示例所示:
Kotlin
Intent(this, LocalService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) }
Java
Intent intent = new Intent(this, LocalService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE);
bindService()
的第一个参数是一个Intent
,它明确指定要绑定的服务。注意:如果您使用 Intent 绑定到
Service
,请务必使用显式 Intent 以确保您的应用安全。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪个服务会响应 Intent,而且用户也无法看到哪个服务启动。从 Android 5.0 (API 级别 21) 开始,如果您使用隐式 Intent 调用bindService()
,系统会抛出异常。- 第二个参数是
ServiceConnection
对象。 - 第三个参数是一个标志,指示绑定的选项——通常是
BIND_AUTO_CREATE
,如果服务尚未运行则创建它。其他可能的值包括BIND_DEBUG_UNBIND
、BIND_NOT_FOREGROUND
,或0
表示无。
其他注意事项
以下是关于绑定服务的一些重要注意事项:
- 始终捕获
DeadObjectException
异常,当连接断开时会抛出此异常。这是远程方法抛出的唯一异常。 - 对象在进程间进行引用计数。
- 您通常在客户端生命周期中对应的启动和终止时刻配对绑定和解除绑定,如以下示例所述:
- 如果您只需要在活动可见时与服务交互,请在
onStart()
期间绑定,并在onStop()
期间解除绑定。 - 如果您希望您的活动即使在后台停止时也能接收响应,请在
onCreate()
期间绑定,并在onDestroy()
期间解除绑定。请注意,这意味着您的活动需要在使用服务时一直运行,即使在后台也是如此。因此,当服务位于另一个进程中时,您会增加该进程的开销,系统更有可能终止它。
注意:您通常不会在活动的
onResume()
和onPause()
回调期间进行绑定和解除绑定,因为这些回调发生在每个生命周期转换时。请将这些转换期间的处理保持在最低限度。此外,如果应用中的多个活动绑定到同一服务,并且在其中两个活动之间进行切换,则服务可能会在当前活动解除绑定(暂停期间)之后、下一个活动绑定(恢复期间)之前被销毁并重新创建。有关活动如何协调其生命周期的这种活动转换,请参阅活动生命周期。
- 如果您只需要在活动可见时与服务交互,请在
有关展示如何绑定到服务的更多示例代码,请参阅 RemoteService.java
类,该类位于 ApiDemos 中。
管理绑定服务的生命周期
当服务与所有客户端解除绑定时,Android 系统会销毁它(除非它是使用 startService()
启动的)。因此,如果您的服务纯粹是绑定服务,您无需管理其生命周期。Android 系统会根据它是否绑定到任何客户端为您管理。
但是,如果您选择实现 onStartCommand()
回调方法,则必须显式停止服务,因为该服务现在被视为已启动。在这种情况下,服务会一直运行,直到服务使用 stopSelf()
停止自身,或另一个组件调用 stopService()
,无论它是否绑定到任何客户端。
此外,如果您的服务已启动并接受绑定,那么当系统调用您的 onUnbind()
方法时,如果您希望在下次客户端绑定到服务时收到 onRebind()
调用,则可以选择返回 true
。onRebind()
返回 void,但客户端仍然在其 onServiceConnected()
回调中接收 IBinder
。下图说明了这种生命周期的逻辑。

图 1. 已启动且允许绑定的服务的生命周期。
有关已启动服务的生命周期的更多信息,请参阅服务概览。