[翻译]API Guides - Bound Services

官方文档原文地址:http://developer.android.com/guide/components/bound-services.html

 

一个Bound Service是一个客户端-服务器接口的服务。一个Bound Service允许组件(像activity)绑定一个service,发送请求,接受结果,甚至进行进程间通信。一个Bound Service通常只在他向其它组件提供服务的时候运行,不会无法确定的运行在后台的。

这篇文档将展示你如何去创建一个Bound service,包括如何绑定一个bound service到其它的应用程序组件上。然而,你也应该关注Service的文档来获取更多关于常规service的额外的内容,比如如何通过service发送一条通知啦,让service在前台运行啦等等。
基础
一个Bound service是Service类的一个实现,它允许其它的应用程序组件去绑定它,并且与它交互。为了能提供与service的绑定,你必须实现onBind()回调方法。这个方法返回一个IBinder对象,这个对象定义了一个客户端可以与service进行交互的接口。
一个客户端可以通过调用bindService()来与service进行绑定。当这个方法调用的时候,它必须提供一个ServiceConnection的实现,这个对象监控着service的连接。bindService()马上就调用完成,并且没有任何返回值,但是当Android系统创建客户端与服务之间的连接时,它会调用ServiceConnection的onServiceConnected()方法,去传递一个用于客户端与service交互的IBinder对象。
多个客户端可以同时连接service。系统只在第一个客户端绑定的时候会调用你的service的onBind()方法去获取一个IBinder对象。然后系统会传递同一个IBinder给其它的客户端,不会再调用onBind()了。
当最后一个客户端解绑了service,系统就会摧毁这个service(除非service也通过startService()启动)。
当你要实现自己的Bound Service,最重要的部分是你要定义onBind()返回的接口。这里有几种不同的方式去定义service的IBinder接口,下面的部分将慢慢讨论技术细节。
创建一个Bound Service
当创建一个绑定的service的时候,你必须提供一个IBinder用于客户端与service进行交互。这里有三种定义这个接口的方式:
Extending the Binder class
如果你的service对你的应用来说是私有的,并且与客户端运行在相同的进程里,你应该通过扩展Binder类来实现你的接口,然后通过onBind()返回一个实例即可。客户端接收Binder,并且可以直接使用它的公有方法,这些方法可能是在Binder里面,甚至可以是service里面的。
当你的service仅仅是在后台完成一些工作的时候,这种方式是最优先的选择。你不应该使用这种方式的唯一原因就是你的service同时需要被其他的应用程序使用。
Using a Messenger
如果你需要你的接口跨进程工作,你可以通过Messenger来为你的service创建接口。在这种方式里,Service需要定义一个Handler来响应不同类型的Message对象。这个Handler是Meesager能把IBinder对象分享给客户端,允许客户端通过使用Message来发送指明的基础。另外,客户端可以定义自己的Messenger,所以service能够发送返回消息。
这是最简单的进程间通信的方式,因为Messenger对垒总是一个单线程的,所以你不得不去更好的设计service让它线程安全。
Using AIDL
AIDL(Android Interface Definition Language)执行了把对象分解成能让操作系统理解的原子功能的所有工作,这样操作系统便可以安排它们完成进程间通信。前一个技术方案,使用Messenger,实际上是基于AIDL的。如上面所提到的,Messenger在单线程里创建一个处理所有客户端发送的请求的队列,所以service一次只能接受一个请求。如果你想同时处理多个请求,你可以直接使用AIDL。这种情况下,你的service必须是多线程的,并且线程安全。
想直接使用AIDL,你必须创建一个.aidl文件用来定义程序的接口。Android SDK工具使用这个文件,把它生成为一个实现了接口并且处理进程间通信的抽象类,然后你可以在你的service中扩展它。
笔记:大多数应用不应该使用AIDL来创建Bound Service的,因为它可能要求多线程的处理能力,这直接的结果就是导致非常复杂的实现。所以,AIDL不适合大多数应用,这篇文档不会讨论如何在你的service里使用它。如果你想要使用它的话,请参考AIDL的文档。
Extending the Binder class
如果你的service只是在本地应用上使用,不需要跨进程工作,你可以实现你自己的Binder类来为客户端提供直接访问service里面公有方法的接口。
笔记:这样的工作只在客户端与service是在同一个应用,同一个进程里,这也是最常见的情况。比如说,这种方式对于一个音乐应用来说将会很好用,它需要绑定一个service到它activity的上用来在后台播放音乐。
接下来是如何完成它:
1.在你的service里创建一个Binder的实例:
包含允许客户端调用的公有方法。
返回当前service的实例,它有允许客户端调用的公有方法。
或者返回service所持有的其它类的实例,里面有允许客户端调用的公有方法。
2.在onBind()方法里返回Binder的实例。
3.在客户端里,从onServiceConnected()方法里接收Binder对象,然后调用Bound Service所提供的方法。
笔记:service与客户端必须在同一个应用里的原因就是客户端可以强制转型返回过来的对象与属性,这样就可以调用它的API了。service与客户端必须在同一个进程里,这是因为技术上不允许执行任何跨进程的通信。
接下的例子,service提供了让客户端直接使用的方法。
publicclassLocalServiceextendsService{// Binder given to clientsprivatefinalIBinder mBinder =newLocalBinder();// Random number generatorprivatefinalRandom mGenerator =newRandom();/*** 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.*/publicclassLocalBinderextendsBinder{LocalService getService(){// Return this instance of LocalService so clients can call public methodsreturnLocalService.this;}}@OverridepublicIBinder onBind(Intent intent){return mBinder;}/** method for clients */publicint getRandomNumber(){return mGenerator.nextInt(100);}
}

 

LocalBinder给客户端提供了getService()方法,用来获取当前LocalService的实例。这就允许了客户端直接调用service里的公有方法。上面的例子里,客户端可以调用service里面的getRandomNumber()方法。
接下来是一个绑定了LocalService的Activity,当按钮按下的时候,调用getRandomNumber()。
publicclassBindingActivityextendsActivity{LocalService mService;boolean mBound =false;@Overrideprotectedvoid onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);}@Overrideprotectedvoid onStart(){super.onStart();// Bind to LocalServiceIntent intent =newIntent(this,LocalService.class);bindService(intent, mConnection,Context.BIND_AUTO_CREATE);}@Overrideprotectedvoid 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) */publicvoid 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() */privateServiceConnection mConnection =newServiceConnection(){@Overridepublicvoid 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;}@Overridepublicvoid onServiceDisconnected(ComponentName arg0){mBound =false;}};
}

 

上面的例子展示了客户端如何绑定service,使用ServiceConnection的实现和onServiceConnected()回调方法。接下来的部分,提供了更多的关于绑定service的信息。
笔记:上面的例子并没有明确的从service那里解绑,但是所有的客户端都应该在合适的时候解绑(比如当Activity pause的时候)。
想要查看更多的代码,在ApiDemos里查看LocalService.java类与LocalServiceActivity.java类。
Using Messenger
如果你需要你的service与其他的进程进行通信,你可以使用Messenger来为你的service提供接口。这项技术允许你不使用AIDL来完成进程间通信。
下面是使用Messenger的几个步骤:
  • service实现一个Handler为从客户端的每一个调用接收回调。
  • Hander用来创建Messenger对象。
  • Messenger创建IBinder对象,用于客户端从service的onBind()方法了获取IBinder。
  • 客户端使用IBinder来初始化Messenger,这样客户端可以使用它给service发送Message对象。
  • service从它的handle里接收每一个Message,准确的说,是在handleMessage()方法里。
在这种方式里,没有任何在service里的方法给客户端调用。取而代之的是,客户端发送消息给service的handler获取。
接下来是一个使用Messenger的简单例子:
publicclassMessengerServiceextendsService{/** Command to the service to display a message */staticfinalint MSG_SAY_HELLO =1;/*** Handler of incoming messages from clients.*/classIncomingHandlerextendsHandler{@Overridepublicvoid 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.*/finalMessenger mMessenger =newMessenger(newIncomingHandler());/*** When binding to the service, we return an interface to our messenger* for sending messages to the service.*/@OverridepublicIBinder onBind(Intent intent){Toast.makeText(getApplicationContext(),"binding",Toast.LENGTH_SHORT).show();return mMessenger.getBinder();}
}

 

注意Handler里面的handleMessage()方法,servie用它来获取消息对象,并且基于what成员属性来决定做什么。
客户端所要做的就是基于IBinder对象创建一个Messenger,使用这个对象通过send()方法来发送消息。下面是个简单的例子,绑定了service的activity发送MSG_SAY_HELLO消息给service:
publicclassActivityMessengerextendsActivity{/** 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.*/privateServiceConnection mConnection =newServiceConnection(){publicvoid 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 =newMessenger(service);mBound =true;}publicvoid 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;}};publicvoid 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();}}@Overrideprotectedvoid onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);}@Overrideprotectedvoid onStart(){super.onStart();// Bind to the servicebindService(newIntent(this,MessengerService.class), mConnection,Context.BIND_AUTO_CREATE);}@Overrideprotectedvoid onStop(){super.onStop();// Unbind from the serviceif(mBound){unbindService(mConnection);mBound =false;}}
}

 

注意这个例子并没有展示service如何回应客户端。如果你想要service去响应客户端,那么你需要在客户端里也创建一个Messenger。当客户端接收onServiceConnected()回调的时候,它向service发送一个消息,它包括了客户端的send()方法中的replyTo参数的Messenger。
你能在MessengerService.java和MessengerServiceActivities.java中看到如何创建一个双向的消息机制。
绑定Service
应用程序组件(客户端)可以通过调用bindService()方法来绑定一个service。Android系统会调用service的onBind()方法,这个方法会返回一个IBinder对象用于和客户端进行交互。
绑定的过程是异步的。bindService()很快就运行结束,并且不会返回一个IBinder对象给客户端。为了接受IBinder,客户端必须创建一个ServiceConnection的实例,然后把它传给bindService()。ServiceConnection包含了供系统调用的传递IBinder的回调方法。
笔记:只有activities,services和content providers可以绑定service,你不可以给一个broadcast receiver绑定一个service。
所以,想要给你的客户端绑定service,你必须:
1.实现ServiceConnection
你的实现必须覆写两个回调方法:
onServiceConnected()
系统调用这个方法来传递由service的onBind()方法返回的IBinder对象。
onServiceDisconnected()
Android系统调用这个方法,是当service被意外的终止了,比如service卡死了,或者被系统杀掉了。当客户端解绑的时候,这个方法是不会被调用的。
2.调用bindService(),并传一个ServiceConnection对象。
3.当系统调用你的onServiceConnected()方法的时候,你可以开始通过在接口中定义的方法来使用service。
4.要断开与service的连接,调用unbindService()。
当你的客户端被销毁的时候,它将会解绑service,但是,你应该在你完成于service的交互或者你的activity暂停的时候去解绑service,因为这样当service没有被使用的时候可以停下来(更多关于何时去绑定与解绑的讨论在下面)。
下面的例子展示了客户端连接到上面extending the Binder class所创建的service,所必须做的就是把返回的IBinder对象强制转型成LocalService的实例:
LocalService mService;
privateServiceConnection mConnection =newServiceConnection(){// Called when the connection with the service is established
    publicvoid 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
    publicvoid onServiceDisconnected(ComponentName className){Log.e(TAG,"onServiceDisconnected");mBound =false;}
};

 

客户端可以通过给bindService()方法放一个ServiceConnection参数来绑定一个service:
Intent intent =newIntent(this,LocalService.class);
bindService(intent, mConnection,Context.BIND_AUTO_CREATE);
  • bindService()的第一个参数是一个指定了绑定哪一个service的Intent对象(虽然intent也可以是隐式的)。
  • 第二个参数是ServiceConnection对象。
  • 第三个参数是一个绑定可选的标示符。通常为了去创建一个不存在的service,应该使用BIND_AUTO_CREATE。其它的值是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或者0。
额外的提示
关于绑定service,这里有一些重要的提示:
  • 你应该总是捕获DeadObjectException异常,这个异常将在连接被破坏的时候抛出,也是唯一一个通过远程方法抛出的异常。
  • 在进程之间,对象的引用被计数。( Objects are reference counted across processes)
  • 你应该总是在你客户端的生命周期里成对的去绑定和解绑,匹配与连接与断开的时候。
    • 如果你仅仅是希望service与你的activity交互在可见的时候,你应该在onStart()方法中绑定,onStop()方法中解绑。
    • 如果你希望你的activity能够在自己停止前一直能接收到反馈结果,你应该在onCreate()里绑定,在onDestroy()里解绑。注意,这意味着你的activity需要一直使用service(甚至是在后台。)所以,如果service在另一个进程里,所以你应该提高你进程的权重,这样系统就不太可能会杀掉它了。
笔记:你不应该在onResume()和onPause()方法里绑定和解绑service,因为这些回调发生在每一次生命周期的转换的时候,你应该把工作的过程放在转换较少的地方。如果你的应用程序里有多个activity要绑定同一个service,这个转换就会发生在两个activity之间,service将会在当前activity解绑的时候(onPause())被摧毁,然后又在下一次绑定之前(onResume())被重新创建。(关于activity是如何协调生命周期来进行activity之间的转换的更多内容,请查看Activiies的文档。)
管理Bound Service的生命周期
当service从所有的客户端上解绑,系统就会销毁它(除非它同时通过了onStartCommand()方法来启动)。所以,你不必要去管理一个仅仅是Bound Service的生命周期。
然后,如果你选择去实现onStartCommand()方法,你就必须明确的停止service,这时service被认为是started。在这种情况下,service会一直运行,直到service通过stopSelf()或者其他组件通过stopService()来停止它。
另外,如果你的service是started的,并且接受了绑定。那么当系统调用你的onBind()方法的时候,你可以选择返回true,这样你可以在下次需要绑定的时候调用onRebind()方法。onRebind()方法什么都不反悔,但是客户端仍然可以在onServiceConnected()方法接受到IBinder对象,下面的图就展示了这种service的生命周期的逻辑。
关于更多started service的信息,请参考Services文档。

转载于:https://www.cnblogs.com/kross/p/3423877.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/473818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sklearn数据集与估计器

sklearn数据集与估计器 1 sklearn数据集 (1)数据来源:大多数以文件的形式 (csv文件..), 因为mysql有性能瓶颈、读取速度遭到限制,数据大的时候很费时间 (2)读取数据的工具&#…

LeetCode 935. 骑士拨号器(动态规划)

文章目录1. 题目2. 解题1. 题目 国际象棋中的骑士可以按下图所示进行移动: 这一次,我们将 “骑士” 放在电话拨号盘的任意数字键(如上图所示)上, 接下来,骑士将会跳 N-1 步。每一步必须是从一个数字键跳到…

k近邻算法(KNN)-分类算法

k近邻算法(KNN)-分类算法 1 概念 定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。 k-近邻算法采用测量不同特征值之间的距离来进行分类。 2 优缺点 优点:简单&a…

LeetCode 822. 翻转卡片游戏(哈希)

文章目录1. 题目2. 解题1. 题目 在桌子上有 N 张卡片,每张卡片的正面和背面都写着一个正数(正面与背面上的数有可能不一样)。 我们可以先翻转任意张卡片,然后选择其中一张卡片。 如果选中的那张卡片背面的数字 X 与任意一张卡片…

朴素贝叶斯算法-分类算法

朴素贝叶斯算法-分类算法 1 概率基础 概率定义为一件事情发生的可能性 联合概率:包含多个条件,且所有条件同时成立的概率,记作P(A,B) 条件概率:事件A在另一个事件B已经发生条件下的发送概率,记作P(A|B) 在A1,A2相…

django简介及环境搭建

MVC简介 MVC框架的核心思想是:解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容 M全拼为Model,主要封装对数据库层的访问,内嵌ORM框架,实现面向对象的编程来操作数据…

LeetCode 1312. 让字符串成为回文串的最少插入次数(区间DP)

文章目录1. 题目2. 解题1. 题目 给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。 请你返回让 s 成为回文串的 最少操作次数 。 「回文串」是正读和反读都相同的字符串。 示例 1: 输入:s "zzazz" 输出&…

Django创建项目

创建项目的名称为test1,完成“图书-英雄”信息的维护,创建应用名称为booktest 创建项目 cd /home/Desktop/ mkdir pytest cd pytest 创建项目的命令如下: django-admin startproject test1 项目默认目录说明 进入test1目录,查看…

分类模型的评估

分类模型的评估 在许多实际问题中,衡量分类器任务的成功程度是通过固定的性能指标来获取。一般最常见使用的是准确率,即预测结果正确的百分比,方法为estimator.score() 1 混淆矩阵 有时候,我们关注的是样本是否被正确诊断出来。…

LeetCode 996. 正方形数组的数目(回溯+剪枝)

文章目录1. 题目2. 解题1. 题目 给定一个非负整数数组 A,如果该数组每对相邻元素之和是一个完全平方数,则称这一数组为正方形数组。 返回 A 的正方形排列的数目。两个排列 A1 和 A2 不同的充要条件是存在某个索引 i,使得 A1[i] ! A2[i]。 …

[Java]java反射随笔

类是面向对象的灵魂,一切事物都可以以类来抽象。 在java使用过程中,我们可能会经常用到一个反射的知识,只是别人都封装好的,如jdbc的加载驱动类有一句Class.for(“…jdbc…”).newInstance.当然框架也是离不开了反射,s…

模型的选择与调优

模型的选择与调优 1 交叉验证 1.1 交叉验证过程 交叉验证:为了让被评估的模型更加准确可信 交叉验证的意义:为了使一部分数据即当成验证集,也做过训练集,更加准确得出准确率,把最后的准确率取平均值。 注意&#…

Django创建图书管理项目(完整版

一、创建项目test1及应用booktest 创建应用booktest 二、安装应用 在test1/settings.py中INSTALLED_APPS下添加应用的名称就可以完成安装 初始项目的INSTALLED_APPS如下图: 在元组中添加一个新的项,当前示例为booktest booktest, 三、设计模型 1.定义模型…

05.序列模型 W3.序列模型和注意力机制

文章目录1. 基础模型2. 选择最可能的句子3. 集束搜索(Beam Search)4. 改进集束搜索5. 集束搜索的误差分析6. Bleu 得分7. 注意力模型直观理解8. 注意力模型9. 语音识别(Speech recognition)10. 触发字检测(Trigger Wor…

决策树-分类算法

决策树-分类算法 1 认识决策树 决策树是一种基本的分类方法,也可以用于回归。我们一般只讨论用于分类的决策树。决策树模型呈树形结构,在分类问题中,表示基于特征对实例进行分类的过程,它可以认为是if-then规则的集合。在决策树…

在Visual Studio中利用NTVS创建Pomelo项目

刚看新闻,才知道微软发布了Node.js Tools for Visual Studio(NTVS),受够了WebStorm输入法Bug的困扰,这下终于可以解脱了。以Pomelo为例,运行命令:pomelo init pomelo_test,生成一个默…

LeetCode 834. 树中距离之和(树上DP)*

文章目录1. 题目2. 解题1. 题目 给定一个无向、连通的树。树中有 N 个标记为 0...N-1 的节点以及 N-1 条边 。 第 i 条边连接节点 edges[i][0] 和 edges[i][1] 。 返回一个表示节点 i 与其他所有节点距离之和的列表 ans。 示例 1: 输入: N 6, edges [[0,1],[0,2],[2,3],[…

随机森林-集成学习方法(分类)

随机森林-集成学习方法(分类) 1 集成学习方法 集成学习通过建立几个模型组合的来解决单一预测问题。它的工作原理是生成多个分类器/模型,各自独立地学习和作出预测。这些预测最后结合成单预测,因此优于任何一个单分类的做出预测…

05.序列模型 W3.序列模型和注意力机制(作业:机器翻译+触发词检测)

文章目录作业1:机器翻译1. 日期转换1.1 数据集2. 用注意力模型进行机器翻译2.1 注意力机制3. 可视化注意力作业2:触发词检测1. 数据合成:创建语音数据集1.1 听一下数据1.2 音频转频谱1.3 生成一个训练样本1.4 全部训练集1.5 开发集2. 模型2.1…

线性回归分析-回归算法

线性回归分析-回归算法 1 回归算法 机器学习中监督学习算法分为分类算法和回归算法两种,其实就是根据类别标签分布类型为离散型、连续性而定义的。回归算法用于目标是连续的,在指定区间内可以是任意一个数值,针对的是数值型的样本&#xff0…