绑定服务是客户端-服务器接口中的服务器。可让组件 例如将 activity 绑定到服务、发送请求、接收响应以及执行 进程间通信 (IPC)。绑定服务通常只在为其他应用组件提供服务时处于活动状态,不会无限期在后台运行。
本文档介绍了如何创建绑定服务,包括 从其他应用组件发送到服务如需进一步了解 例如,如何从某项服务传送通知以及如何将该服务设为运行 请参阅 服务概览。
基础知识
绑定服务是 Service
类的实现,可让
那么其他应用就会绑定到该容器并与其交互。要为
服务,您需要实现 onBind()
回调方法。此方法会返回一个 IBinder
对象,该对象定义的编程接口可供客户端用来与服务进行交互。
绑定到已启动的服务
正如服务概览中所述,
则可以创建一个同时具有已启动和已绑定两种状态的服务也就是说,您可以
调用 startService()
,以允许
服务就会无限期运行您还可以通过以下方法让客户端绑定到该服务
正在调用 bindService()
。
如果您允许服务同时启动和绑定,那么当服务启动时,
当所有客户端都取消绑定后,系统不会销毁该服务。
而必须由您通过调用 stopSelf()
或 stopService()
显式停止服务。
尽管您通常应实现 onBind()
或 onStartCommand()
,但有时也需要同时实现这两种方法。例如,音乐播放器可能发现运行其服务很有用
并且提供绑定关系这样一来,activity 便可启动服务以播放某些
音乐,即使用户离开应用,音乐播放也不会停止。然后,当用户返回应用时,Activity 便能绑定到服务,重新获得播放控制权。
如需详细了解向已启动服务添加绑定时的服务生命周期, 请参阅管理绑定服务的生命周期部分。
客户端通过调用 bindService()
绑定到服务。调用时,它必须提供 ServiceConnection
的实现,后者会监控与服务的连接。调用
bindService()
指示
请求的服务是否存在,以及是否允许客户端访问该服务。
时间
Android 系统会在客户端与服务之间创建连接,
致电 onServiceConnected()
(在 ServiceConnection
上)。通过
onServiceConnected()
方法包含 IBinder
参数,客户端随后会使用该参数与绑定服务进行通信。
您可以将多个客户端同时连接到某项服务。但是,系统会缓存 IBinder
服务通信通道。换言之,系统会调用服务的 onBind()
方法生成 IBinder
方法仅在第一个
客户端绑定然后,系统会将该 IBinder
传递至绑定到同一服务的所有其他客户端,无需再次调用 onBind()
。
当最后一个客户端取消与服务的绑定时,系统会销毁该服务,除非
服务是使用 startService()
启动的。
在实现绑定服务的过程中,最重要的环节是定义 onBind()
回调方法所返回的接口。以下
部分讨论了使用 YAML 文件定义服务
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(); } }
handleMessage()
方法(在
Handler
是服务用于接收传入 Message
的位置
并根据 what
成员决定要执行的操作。
客户端只需根据服务返回的 IBinder
创建 Messenger
,然后使用 send()
发送消息。例如,以下 activity 绑定到
并将 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
,并在其 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
异常 在连接中断的情况下使用。这是远程方法抛出的唯一异常。 - 对象是跨进程计数的引用。
- 在
匹配客户端生命周期内的启动和撤回时刻,如
示例:
<ph type="x-smartling-placeholder">
- </ph>
- 如果您只需要在 activity 可见时与服务交互,请在
onStart()
期间绑定,在onStop()
期间取消绑定。 - 如果您希望 activity 在
后台、在
onCreate()
期间绑定以及取消绑定onDestroy()
期间。请注意,这意味着 activity 在其整个运行过程中(甚至是在后台运行)都需要使用该服务,因此 服务在另一个进程中,那么增加该进程的权重, 被系统终止的可能性更大。
注意:您通常不绑定和取消绑定 的
onResume()
和onPause()
回调期间,因为这些回调在 生命周期转换。 应尽量减少在这些转换过程中进行的处理。此外,如果 应用中的多个活动绑定到同一服务 之间的过渡 那么服务可能会被销毁并重新创建为当前的当前 activity activity 解除绑定 (暂停期间)在下一个绑定之前(恢复期间)。此 activity 过渡有助于您了解如何 activity 生命周期中介绍了 activity 如何协调其生命周期。
- 如果您只需要在 activity 可见时与服务交互,请在
有关说明如何绑定到服务的更多示例代码,请参阅
RemoteService.java
课程中的 ApiDemos。
管理绑定服务的生命周期
当某个服务与所有客户端解除绑定时,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=7&hl=nb)
图 1. 已启动并且还允许绑定的服务的生命周期。
如需详细了解已启动服务的生命周期,请参阅服务概览。