绑定服务

绑定服务是客户端 - 服务器接口中的服务器。绑定服务允许组件(例如Activity)绑定到服务,发送请求,接收响应,甚至执行进程间通信(IPC)。绑定服务通常仅在其服务于另一个应用程序组件时才存在,并且不会无限期地在后台运行。

本文档介绍如何创建绑定服务,包括如何从其他应用程序组件绑定到服务。但是,您还应该参阅“服务”文档以获取有关服务的其他信息,例如如何从服务传递通知,将服务设置为在前台运行等等。

基础


绑定服务是Service类的实现,它允许其他应用程序绑定到它并与之交互。要为服务提供绑定,必须实现onBind()回调方法。此方法返回IBinder对象,该对象定义客户端可用于与服务交互的编程接口。

客户端可以通过调用bindService()绑定到服务。如果是这样,它必须提供ServiceConnection的实现,它监视与服务的连接。 bindService()方法在没有值的情况下立即返回,但是当Android系统在客户端和服务之间创建连接时,它会调用ServiceConnection上的onServiceConnected(),以提供客户端可用于与服务通信的IBinder。

多个客户端可以立即连接到该服务。但是,只有在第一个客户端绑定时,系统才会调用服务的onBind()方法来检索IBinder。然后,系统将相同的IBinder传递给任何绑定的其他客户端,而无需再次调用onBind()。

当最后一个客户端从服务解除绑定时,系统会销毁该服务(除非该服务也是由startService()启动的)。

实现绑定服务时,最重要的部分是定义onBind()回调方法返回的接口。您可以通过几种不同的方式定义服务的IBinder借口,以下部分将讨论每种技术。

绑定到已启动的服务

如“服务”文档中所述,您可以创建既启动又绑定的服务。也就是说,可以通过调用startService()来启动服务,该服务允许服务无限期地运行,并且还允许客户端通过调用bindService()来绑定到服务。

如果您确实允许启动和绑定服务,那么当服务启动时,系统不会在所有客户端解除绑定时销毁服务。相反,您必须通过调用stopSelf()或stopService()来显式停止服务。

虽然您通常应该实现onBind()或onStartCommand()其中一个,但有时需要同时实现它们。例如,音乐播放器可能会发现允许其服务无限期运行并提供绑定很有用。这样,活动可以启动服务以播放一些音乐,并且即使用户离开应用程序,音乐也继续播放。然后,当用户返回到应用程序时,活动可以绑定到服务以重新获得对回放的控制。

请务必阅读有关管理绑定服务生命周期的部分,以获取有关将绑定添加到已启动服务时的服务生命周期的更多信息。

创建绑定服务


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

  • 扩展Binder类

如果您的服务对您自己的应用程序是私有的并且在与客户端相同的进程中运行(这是常见的),您应该通过扩展Binder类并从onBind()返回它的实例来创建您的接口。客户端接收Binder并可以使用它直接访问Binder实现甚至服务中可用的公共方法。

当您的服务仅仅是您自己的应用程序的后台工作程序时,这是首选技术。您不以这种方式创建接口的唯一原因是因为您的服务被其他应用程序或跨单独的进程使用。

  • 使用Messenger

如果您需要在不同进程中使用接口,则可以使用Messenger为服务创建接口。通过这种方式,服务定义了一个响应不同类型的Message对象的Handler。此Handler是Messenger的基础,然后可以与客户端共享IBinder,允许客户端使用Message对象向服务发送命令。此外,客户端可以定义自己的Messenger,以便服务可以发回消息。

这是执行进程间通信(IPC)的最简单方法,因为Messenger将所有请求排队到一个线程中,这样您就不必将服务设计为线程安全的。

  • 使用AIDL

AIDL(Android接口定义语言)执行所有工作,将对象分解为操作系统可以理解的原语,并跨进程对其进行编组以执行IPC。使用Messenger的先前技术实际上是基于AIDL作为其底层结构。如上所述,Messenger在单个线程中创建所有客户端请求的队列,因此服务一次接收一个请求。但是,如果您希望服务同时处理多个请求,则可以直接使用AIDL。在这种情况下,您的服务必须能够进行多线程并且构建为线程安全的。

要直接使用AIDL,必须创建一个定义编程接口的.aidl文件。 Android SDK工具使用此文件生成实现接口并处理IPC的抽象类,然后您可以在服务中扩展该类。

注意:大多数应用程序不应使用AIDL来创建绑定服务,因为它可能需要多线程功能,并且可能导致更复杂的实现。因此,AIDL不适合大多数应用程序,本文档不讨论如何将其用于您的服务。如果您确定需要直接使用AIDL,请参阅AIDL文档。

扩展Binder类

如果您的服务仅由本地应用程序使用,并且不需要跨进程工作,那么您可以实现自己的Binder类,使您的客户端可以直接访问服务中的公共方法。

注意:仅当客户端和服务位于同一应用程序和进程中时才有效,这是最常见的。例如,这适用于需要将活动绑定到其后台播放音乐的服务的音乐应用程序。

以下是如何设置它:

1.在您的服务中,创建一个Binder实例:

  • 包含客户端可以调用的公共方法
  • 返回当前的Service实例,该实例具有客户端可以调用的公共方法
  • 或者,使用客户端可以调用的公共方法返回由服务托管的另一个类的实例

2.从onBind()回调方法返回此Binder实例。

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

注意:服务和客户端必须位于同一应用程序中的原因是客户端可以强制转换返回的对象并正确调用其API。服务和客户端也必须处于同一个进程中,因为此技术不会跨进程执行任何编组。

例如,这是一个服务,通过Binder实现为客户提供对服务中方法的访问:

public class LocalService extends Service {// Binder given to clientsprivate final IBinder mBinder = new LocalBinder();// Random number generatorprivate 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 methodsreturn LocalService.this;}}@Overridepublic IBinder onBind(Intent intent) {return mBinder;}/** method for clients */public int getRandomNumber() {return mGenerator.nextInt(100);}
}

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

这是一个绑定到LocalService的activity,并在单击按钮时调用getRandomNumber():

public class BindingActivity extends Activity {LocalService mService;boolean mBound = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);}@Overrideprotected void onStart() {super.onStart();// Bind to LocalServiceIntent intent = new Intent(this, LocalService.class);bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}@Overrideprotected void onStop() {super.onStop();// Unbind from the serviceif (mBound) {unbindService(mConnection);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 were something that might hang, then this request should// occur 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 mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName className,IBinder service) {// We've bound to LocalService, cast the IBinder and get LocalService instanceLocalBinder binder = (LocalBinder) service;mService = binder.getService();mBound = true;}@Overridepublic void onServiceDisconnected(ComponentName arg0) {mBound = false;}};
}

上面的示例显示了客户端如何使用ServiceConnection的实现和onServiceConnected()回调绑定到服务。下一节提供有关绑定到服务的此过程的更多信息。

注意:在上面的示例中,onStop()方法取消绑定客户端与服务的绑定。客户应在适当的时间解除服务绑定,如附加说明中所述。

有关更多示例代码,请参阅ApiDemos中的LocalService.java类和LocalServiceActivities.java类。

使用Messenger

与AIDL相比

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

对于大多数应用程序,该服务不需要执行多线程,因此使用Messenger允许服务一次处理一个呼叫。如果您的服务是多线程的,那么您应该使用AIDL来定义您的界面。

如果您需要服务与远程进程通信,则可以使用Messenger为您的服务提供接口。此技术允许您执行进程间通信(IPC),而无需使用AIDL。

以下是如何使用Messenger的摘要:

  • 该服务实现一个Handler,它接收来自客户端的每个调用的回调。
  • Handler用于创建Messenger对象(它是对Handler的引用)。
  • Messenger创建一个IBinder,服务从onBind()返回到客户端。
  • 客户端使用IBinder实例化Messenger(引用服务的Handler),客户端使用它将Message对象发送到服务。
  • 该服务在其Handler中接收每个Message,具体来说,在handleMessage()方法中。

这样,客户端就没有“方法”来调用服务。相反,客户端提供服务在其处理程序中接收的“消息”(消息对象)。

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

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.*/class IncomingHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_SAY_HELLO:Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();break;default:super.handleMessage(msg);}}}/*** Target we publish for clients to send messages to IncomingHandler.*/final Messenger mMessenger = new Messenger(new IncomingHandler());/*** When binding to the service, we return an interface to our messenger* for sending messages to the service.*/@Overridepublic IBinder onBind(Intent intent) {Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();return mMessenger.getBinder();}
}

请注意,Handler中的handleMessage()方法是服务接收传入消息的位置,并根据成员决定要执行的操作。

客户端需要做的就是根据服务返回的IBinder创建一个Messenger,并使用send()发送消息。例如,这是一个绑定到服务并将MSG_SAY_HELLO消息传递给服务的简单活动:

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 mBound;/*** 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);mBound = 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;mBound = false;}};public void sayHello(View v) {if (!mBound) return;// Create and send a message to the service, using a supported 'what' valueMessage msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);try {mService.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);}@Overrideprotected void onStart() {super.onStart();// Bind to the servicebindService(new Intent(this, MessengerService.class), mConnection,Context.BIND_AUTO_CREATE);}@Overrideprotected void onStop() {super.onStop();// Unbind from the serviceif (mBound) {unbindService(mConnection);mBound = false;}}
}

请注意,此示例未显示服务如何响应客户端。如果您希望服务响应,则还需要在客户端中创建Messenger。然后,当客户端收到onServiceConnected()回调时,它会向send()方法的replyTo参数中包含客户端Messenger的服务发送一条消息。

您可以在MessengerService.java(服务)和MessengerServiceActivities.java(客户端)示例中看到如何提供双向消息传递的示例。

绑定到服务


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

绑定是异步的。 bindService()立即返回,不会将IBinder返回给客户端。要接收IBinder,客户端必须创建ServiceConnection实例并将其传递给bindService()。 ServiceConnection包含系统调用以传递IBinder的回调方法。

注意:只有Activity,服务和内容提供者可以绑定到服务 - 您无法从广播接收器绑定到服务。

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

1.实现ServiceConnection。

您的实现必须覆盖两个回调方法:

onServiceConnected()

系统调用此方法来传递服务的onBind()方法返回的IBinder。

onServiceDisconnected()

当与服务的连接意外丢失时,例如当服务崩溃或被杀死时,Android系统会调用此方法。客户端解除绑定时不会调用此方法。

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

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

4.要断开与服务的连接,请调用unbindService()。

如果当您的应用程序销毁客户端时,您的客户端仍然绑定到服务,则销毁会导致客户端解除绑定。一旦完成与服务的交互,最好解除客户端的绑定。这样做可以使空闲服务关闭。有关绑定和取消绑定的适当时间的详细信息,请参阅其他说明。

例如,以下代码段通过扩展Binder类将客户端连接到上面创建的服务,因此它必须做的就是将返回的IBinder强制转换为LocalService类并请求LocalService实例:

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {// Called when the connection with the service is establishedpublic 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 unexpectedlypublic void onServiceDisconnected(ComponentName className) {Log.e(TAG, "onServiceDisconnected");mBound = false;}
};

通过此ServiceConnection,客户端可以通过将其传递给bindService()来绑定到服务。例如:

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
  • bindService()的第一个参数是一个Intent,它明确命名要绑定的服务(认为intent可能是隐式的)。
  • 第二个参数是ServiceConnection对象。
  • 第三个参数是一个标志,指示绑定的选项。它应该通常是BIND_AUTO_CREATE,以便在服务尚未生效时创建服务。其他可能的值是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或者0表示无。

补充

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

  • 您应始终捕获DeadObjectException异常,这些异常在连接断开时抛出。这是远程方法抛出的唯一异常。
  • 对象是跨进程的引用计数。
  • 在匹配客户生命周期的启动和销毁时,通常应该将绑定和解除绑定配对。例如:

1.如果您只需要在Activity可见时与服务进行交互,则应在onStart()期间绑定并在onStop()期间解除绑定。

2.如果您希望活动即使在后台停止时也会收到响应,那么您可以在onCreate()期间绑定并在onDestroy()期间解除绑定。请注意,这意味着您的活动需要在整个运行时使用该服务(即使在后台运行),因此如果服务在另一个进程中,那么您会增加进程的权重,系统将更有可能杀了它。

注意:在活动的onResume()和onPause()期间通常不应绑定和解除绑定,因为这些回调在每个生命周期转换时发生,您应该将这些转换中发生的处理保持在最低限度。此外,如果应用程序中的多个活动绑定到同一服务,并且其中两个活动之间存在转换,则可能会销毁该服务在当前活动解除绑定(暂停期间)之前(在恢复期间)之前重新创建该服务。 (活动文档中描述了活动如何协调其生命周期的活动转换。)

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

管理绑定服务的生命周期


当一个服务从所有客户端解除绑定时,Android系统会将其销毁(除非它也是以onStartCommand()启动的)。因此,如果服务纯粹是绑定服务,则不必管理服务的生命周期 - Android系统会根据是否绑定到任何客户端来为您管理服务。

但是,如果您选择实现onStartCommand()回调方法,则必须显式停止该服务,因为现在认为该服务已启动。在这种情况下,服务一直运行,直到服务停止,stopSelf()或其他组件调用stopService(),无论它是否绑定到任何客户端。

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

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

有关已启动服务的生命周期的详细信息,请参阅“服务”文档。

Android官方文档—APP组件(Services)(Bound Services)相关推荐

  1. Android官方文档—APP清单(uses-feature)

    <uses-feature> 语法: <uses-featureandroid:name="string"android:required=["true ...

  2. Android官方文档之App Components(Activities)

    Activity是Android四大组件之首,本文将介绍Activity的含义.创建.启动.销毁.生命周期 等. 如需访问官方原文,您可以点击这个链接:<Activities> Activ ...

  3. Android官方文档之App Components(Common Intents)(转载)

    原文地址:http://blog.csdn.net/vanpersie_9987/article/details/51244558#rd Intent的真正强大之处在于它的隐式Intent,隐式Int ...

  4. Android官方文档之App Components(Common Intents)

    Intent的真正强大之处在于它的隐式Intent,隐式Intent需要配合Intent-filters使用,有关这方面的概念和知识,您可以参考我翻译的官方文档:<Android官方文档之App ...

  5. Android官方文档training中英文翻译目录大全:29篇已翻译,45篇未翻译

    Android官方文档training中英文翻译目录大全:29篇已翻译,45篇未翻译 1. Getting Started Building Your First App: 原文: https://d ...

  6. Android官方文档系列(翻译)

    下面一些内容是我翻译Android官方文档相关文章,以及整理的其它内容. 菜鸟上路,一边学习一边整理,若您发现有错误的地方还请不吝赐教. Activity 解读Android之Activity基础知识 ...

  7. 通过gae访问android官方文档

    不知何时,连www.android.com都不能访问了.看最新的android文档和下载最新的android sdk,可以通过appspot代理访问android网站. android官方文档和sdk ...

  8. 转】Fragments (Android官方文档中文版)

    Fragments (Android官方文档中文版) 传送门:http://www.eoeandroid.com/thread-71642-1-1.html

  9. Ant Design 入门-参照官方文档使用组件

    微信小程序开发交流qq群   173683895    承接微信小程序开发.扫码加微信. 先来一个按钮组件使用的对比,官方文档的(不能直接用)和实际能用的. 官网demo: import { Tabl ...

最新文章

  1. memcache-session-manager(flexjson)
  2. JavaScript学习记录总结(四)——js函数的特殊性
  3. 在Linux中挂载Windows端共享权限设定方法和出现报错的解决办法
  4. 一句DOS命令搞定文件合并
  5. 怎样调用另一个html中的元素,html – 使用DIV作为另一个元素的背景
  6. 740. Delete and Earn
  7. LAMP架构,纯文本作品
  8. linkbot 中文教程系列 linkbot破解教程
  9. 细胞自动机 c语言程序,细胞自动机之生命游戏
  10. MathType与Office公式编辑器有什么不同
  11. android 代码混淆 反编译,Android的反编译和代码混淆
  12. 高中计算机教育类文章,高中信息技术的教学论文
  13. 电脑Win7系统桌面图标太大怎么调小
  14. Coverity代码静态检测工具导出Excel文件
  15. 微软服务器系统补丁kb2919355,Windows 8.1补丁KB2919355无法安装的解决方法
  16. 2 什么是计算机网络设置密码,怎么设置电脑网络密码
  17. 计算机工作自动化的特点,计算机内部自动化操作等特点.doc
  18. 英语语法 定冠词与专有名词
  19. 【前端基础知识】讲清楚正则表达式——第四期(4/5)
  20. Windows使用dos窗口打开应用

热门文章

  1. 腾讯云图 数据源 api
  2. 原子操作、锁、同步实现原理
  3. windows10系统纯净版下载地址
  4. 《Linux C编程环境》 课程大实验 及近期练习题:计算器,复写机,目录树创建,批处理执行器,扫雷
  5. 漫威系列-《复联4》
  6. 计算机游戏教学法的创新之处,游戏教学法在小学英语课堂论文开题报告的创新点...
  7. 移动开发之三种近场通信
  8. C# 学习笔记04-15
  9. 7.5 《丰田模式》阅读笔记和感悟
  10. 好嗨游戏:战火重燃!2019LPL夏季赛精彩看点全盘点! || 附夏季赛赛程表