绑定服务是客户端-服务器接口中的服务器。它允许 activity 等组件绑定到服务、发送请求、接收响应,以及执行进程间通信 (IPC)。绑定服务通常只在为其他应用组件提供服务时处于活动状态,不会无限期在后台运行。
本文档介绍了如何创建绑定服务,包括如何绑定到其他应用组件中的该服务。如需详细了解一般服务(例如,如何从服务传送通知以及如何将服务设置为在前台运行),请参阅 服务概览。
基础知识
绑定服务是 Service
类的实现,可让其他应用与其绑定并进行交互。如需为服务提供绑定,请实现 onBind()
回调方法。此方法会返回一个 IBinder
对象,该对象定义的编程接口可供客户端用来与服务进行交互。
绑定到已启动的服务
如服务概览中所述,您可以创建同时具有已启动和已绑定两种状态的服务。也就是说,您可以通过调用 startService()
来启动某项服务,该服务会无限期地运行。您还可以通过调用 bindService()
让客户端绑定到服务。
如果您允许服务同时启动并绑定,则当服务启动时,系统不会在所有客户端都取消绑定时销毁服务。而必须由您通过调用 stopSelf()
或 stopService()
显式停止服务。
尽管您通常应实现 onBind()
或 onStartCommand()
,但有时也需要同时实现这两种方法。例如,音乐播放器可能会发现让其服务无限期运行并同时提供绑定很有用处。这样,activity 可以启动服务来播放音乐,并且即使用户离开应用,音乐也会继续播放。然后,当用户返回应用时,Activity 便能绑定到服务,重新获得播放控制权。
如需详细了解向已启动服务添加绑定时的服务生命周期,请参阅管理绑定服务的生命周期部分。
客户端通过调用 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
类,该类可让客户端直接访问服务中的公共方法。
注意:只有当客户端和服务处于同一应用和进程内(最常见的情况)时,此方式才有效。例如,这对于需要将 activity 绑定到在后台播放音乐的自有服务的音乐应用非常有效。
以下为设置方式:
- 在您的服务中,创建可执行以下某种操作的
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()
。
点击按钮时,以下 Activity 会绑定到 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()
方法取消了客户端与服务的绑定。
如其他说明部分所述,在适当的时间将客户端与服务取消绑定。
如需查看更多示例代码,请参阅 ApiDemos 中的 LocalService.java
类和 LocalServiceActivities.java
类。
使用 Messenger
如果您需要让服务与远程进程通信,则可使用 Messenger
为您的服务提供接口。借助此方法,您无需使用 AIDL 即可执行进程间通信 (IPC)。
为接口使用 Messenger
比使用 AIDL 更简单,因为 Messenger
会将所有服务调用加入队列。纯 AIDL 接口会同时向服务发送多个请求,然后服务必须处理多线程。
对于大多数应用,服务不需要执行多线程处理,因此使用 Messenger
可让服务一次处理一个调用。如果您的服务必须执行多线程处理,请使用 AIDL 定义接口。
以下是对 Messenger
使用方式的摘要:
- 服务实现一个
Handler
,由其接收来自客户端的每个调用的回调。 - 服务使用
Handler
来创建Messenger
对象(该对象是对Handler
的引用)。 Messenger
创建一个IBinder
,服务通过onBind()
将其返回给客户端。- 客户端使用
IBinder
将Messenger
(它引用服务的Handler
)实例化,然后再用其将Message
对象发送给服务。 - 服务在其
Handler
中(具体而言,是在handleMessage()
方法中)接收每个Message
。
这样,客户端便没有调用服务的方法。相反,客户端会传递服务在其 Handler
中接收的消息(Message
对象)。
下面这个简单的服务实例展示了如何使用 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
消息的 activity:
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
,并在其 send()
方法的 replyTo
参数中加入客户端的 Messenger
。
如需查看如何提供双向消息传递的示例,请参阅 MessengerService.java
(服务)和 MessengerServiceActivities.java
(客户端)示例。
绑定到服务
应用组件(客户端)可通过调用 bindService()
绑定到服务。然后,Android 系统会调用服务的 onBind()
方法,该方法会返回用于与服务交互的 IBinder
。
绑定是异步操作,并且 bindService()
可立即返回,无需将 IBinder
返回给客户端。如要接收 IBinder
,客户端必须创建一个 ServiceConnection
实例,并将其传递给 bindService()
。ServiceConnection
包含一个回调方法,系统通过调用该回调方法来传递 IBinder
。
注意:只有 Activity、服务和内容提供程序可以绑定到服务,您无法从广播接收器绑定到服务。
如要从您的客户端绑定到服务,请按以下步骤操作:
- 实现
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
异常。这是远程方法抛出的唯一异常。 - 对象是跨进程计数的引用。
- 您通常在客户端生命周期的匹配引入 (bring-up) 和退出 (tear-down) 时刻期间配对绑定和取消绑定,如以下示例所示:
- 如果您只需要在 activity 可见时与服务交互,请在
onStart()
期间绑定,在onStop()
期间取消绑定。 - 如果您希望 activity 在后台停止运行的情况下仍可接收响应,请在
onCreate()
期间绑定,在onDestroy()
期间取消绑定。请注意,这意味着您的 activity 在整个运行过程中(即使是在后台运行)都需要使用该服务,因此当该服务位于其他进程中时,您的进程的权重会增加,并且它更容易被系统终止。
注意:通常情况下,不应在 activity 的
onResume()
和onPause()
回调期间绑定和取消绑定,因为这些回调在每次生命周期转换时都会发生。尽可能减少在这些转换期间进行的处理。此外,如果应用中的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则当当前 Activity 在下一个 Activity 绑定(恢复期间)之前取消绑定(暂停期间)时,系统可能会销毁并重新创建服务。activity 生命周期中介绍了这种关于 activity 如何协调其生命周期的 activity 转换。
- 如果您只需要在 activity 可见时与服务交互,请在
如需查看更多显示如何绑定到服务的示例代码,请参阅 ApiDemos 中的
RemoteService.java
类。
管理绑定服务的生命周期
当服务与所有客户端解除绑定时,Android 系统会销毁该服务(除非该服务是使用 startService()
启动的)。因此,如果该服务纯粹是绑定服务,您就不必对其生命周期进行管理。Android 系统会根据此标识符是否绑定到任何客户端来为您管理此标识符。
不过,如果您选择实现 onStartCommand()
回调方法,则必须显式停止该服务,因为该服务现在被视为已启动。在此情况下,服务将一直运行,直到其通过 stopSelf()
自行停止,或其他组件调用 stopService()
(与该服务是否绑定到任何客户端无关)。
此外,如果您的服务已启动并接受绑定,那么当系统调用您的 onUnbind()
方法时,如果您想在客户端下次绑定到服务时收到对 onRebind()
的调用,可以选择返回 true
。onRebind()
返回空值,但客户端仍会在其 onServiceConnected()
回调中接收 IBinder
。下图说明了这种生命周期的逻辑。
![](https://developer.android.google.cn/static/images/fundamentals/service_binding_tree_lifecycle.png?authuser=5&hl=el)
图 1. 已启动并且还允许绑定的服务的生命周期。
如需详细了解已启动服务的生命周期,请参阅服务概览。