Android Service和Binder、AIDL
1.首先理解service的作用和生命周期
由于activity如果切换,那么他就不再运行,那么我们想在玩游戏的时候听播放器中的音乐,activity就应运而生了,这是最常见的一种场景,同时service由于它的优先级比较高,不容易被回收,而且是独立进程,不会阻塞UI线程,因此,可以用来处理一些比较费时的任务。
service起于startService(),终于stopService,如果没有调用stopService,那么,即使调用者结束了,该service也一直存在。
也可以通过bindService来绑定service,unBindService分开并结束service。如果bind的时候没有启动service,那么它会调用service的create方法启动。
多个程序可同时bind同一个service,只有他们都unbind了,这个service就会自动结束。如果有部分unbinder的时候调用stopservice,这个stop也会等到它们全部结束的时候才真的结束。
2.service的种类
由于adroid的独特的线程模型,service被同一个apk调用和不同apk调用原理是不同的,因此分成以下两种:
(1).
本地服务(Local Service):说白了就是在同一个apk内被调用。
(2). 远程服务(Remote
Sercie):被另外一个apk调用。
3.涉及到的内容:
由于android的进程模型,不同的apk不能共用数据,因此如果有需要的话需要通过进程间通讯完成。
进程间通讯(ipc)涉及到三个部分:客户端(调用方),传递数据,服务端(被调用方)。
android的数据传递类似于RPC过程,采用aidl的方式传递,考虑到效率的原因,没有用java内置的serializable,而是采用原始数据拆分组装的parcel方式。
4.场景假设:
一个service提供产生Student数据的功能,作为服务端,一个apk中的activity想获取另一个apk中的一个学生。根据上边的分析,service即使服务端,activity即使客户端,student是要传输的数据,要被包装成pacel传递。
5.根据android思想,理想的传递过程:
student提供拆分成parcel的writeToParcel()方法和根据Parcel组装的构造函数Student(Pacel
p);
一个bissiness Service(bizSvc)用来实现逻辑功能。
一个android
service(adSvc)调用bizService提供功能。
一个MyBinder负责service描述符与bizService的映射(本地服务时供queryLocalInterface查找)和与客户端交互。(如果被本地调用,就查找到相应的service直接操作,不用远程通讯,如果被远程调用,就得负责与远程通讯)
一个Proxy负责在客户端提供transact()方法发送数据。
具体的调用过程如下:
调用的时候,客户端首先调用bindService(new
Intent ("abc"),
serviceConnection,Context.BIND_AUTO_CREATE);激活serviceConnection的onServiceConnected方法,在此方法中获取到一个binder(注:clientBinder,系统给我们的一个与远程进行通信的binder,不是我们刚才自己实现的),此binder能够查找系统中注册的service,如果没有查找到该Service,那么可认定该service是从其他apk获得的,就创建一个此service的静态代理类Proxy,否则,就把这个service返回,供客户端调用。
服务端收到这个Intent后,激活adSvc的onBind方法,创建一个MyBinder返回。之后,这个MyBinder负责与客户端的Proxy通信。
之后,客户端要调用service的方法,可直接调用(本地)或者通过代理调用(远程),这个Proxy调用clientBinder的transact方法,参数为要掉用的方法(TRANSACRION_XX),发送的参数(_data),接受的返回值(_reply),把消息传送给服务端。
服务端收到消息后,调用MyBinder的onTransac方法,根据Proxy传递过来参数,调用bizService不同的方法,并把产生的值组装成Parcel发送回去。之后客户端Proxy会自动调用sudent的相关方法,把数据重新组装,进行下一步处理。
理想的代码如下(这是理想的代码,经过拆分,但是不符合android的aidl规范,不能运行,见下文分析):
客户端代码:
final
IMyBizService bizSvc;
bindService(new Intent("abc"),new
ServiceConnection(){
@Override
public void
onServiceConnected(ComponentName name, IBinder clientBinder) {
if
((binder == null)) {
return null;
}
IInterface iin =
clientBinder.queryLocalInterface(DESCRIPTOR);//如果是查找本地有,就直接返回
if
(((iin != null) && (iin instanceof IMyService))) {
return
((IMyService) iin);
}
return new
Proxy(obj);//否则采用远程代理
}
},
Context.BIND_AUTO_CREATE);
Proxy.java:
public class Proxy
implements IMyBizService {
private IBinder mRemote;
private String
description;
public Proxy(IBinder binder,String description)
{
super();
this.mRemote=binder;
this.description=description;
}
@Override
public
IBinder asBinder() {
return mRemote;
}
public
Student getStudent() throws android.os.RemoteException
{
android.os.Parcel _data =
android.os.Parcel.obtain();
android.os.Parcel _reply =
android.os.Parcel.obtain();
Student _result;
try
{
_data.writeInterfaceToken(description);
mRemote.transact(MyBinder.TRANSACTION_getStudent,
_data,_reply, 0);//发送参数给服务端
_reply.readException();
if ((0 !=
_reply.readInt())) {
_result = new Student(_reply);//接受参数并返回
}
else {
_result = null;
}
} finally
{
_reply.recycle();
_data.recycle();
}
return
_result;
}
}
服务端代码:
MyService.java:
public
class MyService extends Service {
@Override
public IBinder
onBind(Intent intent) {
MyBizServiceImpl svc=new
MyBizServiceImpl();
MyBinder binder= new MyBinder(new
MyBizServiceImpl(),intent.getAction());
svc.setBinder(binder);//(此处有点绕,binder方法需要bizService作为参数实现绑定和调用,而bizSvc需要这个Binder作为参数,用来实现asBind方法,形成了一个环);
return
binder;
}
}
MyBinder.java:
public class
MyBinder extends Binder {
static final int TRANSACTION_getStudent =
(IBinder.FIRST_CALL_TRANSACTION + 1);
private IInterface
intf;
private String descripter;
public
MyBinder(IInterface intf, String descripter)
{
super();
this.intf=intf;
this.descripter=descripter;
this.attachInterface(intf,
descripter);
}
@Override
public boolean
onTransact(int code, Parcel data,Parcel reply, int flags)throws RemoteException
{
IMyBizService myService=(IMyBizService) this.intf;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descripter);
return true;
}
case
TRANSACTION_getStudent:
{
data.enforceInterface(descripter);
Student _result =
myService.getStudent();
reply.writeNoException();
if ((_result !=
null))
{
reply.writeInt(1);
_result.writeToParcel(reply,Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//发送数据给客户端
}
else {
reply.writeInt(0);
}
return
true;
}
}
return super.onTransact(code, data, reply,
flags);
}
}
MyBizService.java:
public class
MyBizServiceImpl implements IMyBizService {
private IBinder
binder;
public void setBinder(IBinder
binder){
this.binder=binder;
}
@Override
public
IBinder asBinder() {
return
binder;
}
@Override
public Student getStudent()
throws RemoteException {
Student st = new
Student();
st.setName("devil");
return
st;
}
}
5
aidl对这个机制的改进:
第4步的代码不能执行,只能作为理解原理和阅读代码用。因为为了安全和效率,aidl的关键类自动生成,不能修改,同时android对aidl的保护也使得需要一些额外的检查。
对比以上代码,android自动生成的代码有以下几个方面不同:
1).MyBinder,IMyBizService,和Proxy通通根据IMyBizService.aidl文件自动生成为IMyBizService.java文件。具体关系如下:
MyBinder和IMyBizService被合并成一个Stub类作为IMyBizService的内部类(直接导致this被滥用,但同时实现了这两个,那么相关方法直接用this就行了,不用形成前文所说的环了)。这个类负责接与客户端交互。而且还有一个额外静态方法asInterface供客户端在引起connection的onConnected时调用,实现根据传入的clientBinder判断本次调用是本地调用还是远程调用,从而确定是否采用Proxy,这样这个方法既然已经实现了,那么我们直接用就行了,无须再写一遍。具体IMyBizService的实现,只需继承Stub类即可。这样,只需在adService中创建MyBinder的时候,也就同时创建了MyBizService.
Proxy作为Stub的内部类(真纠结。。),被asInterface调用,从而进一步被客户端调用。
2).由于自动生成的IMyBizService.java中的Stub的onTransact利用了Student类中的一个CREATE内部域作为转换器,我们必须在Student类中添加并实现这个域,注意:域名必须是CREATE大写。(这个实现有点囧,可能是因为在自动生成文件的时候无法确定这个转换方法的写法)。
6
这个模型一些特例
1).如果本service只作为本地调用,可无须aidl相关操作,服务端的binder直接像stub那样,即使binder又是service,这样,调用方(如Activity)由于与该service在同一个apk中,ServiceConnection中获取的clientBind就是service返回的那个myBinder,这样的话,直接强转即可得到IMyBizService.不用调用queryLocalInterface()方法再查找一次。
2).如果不用额外调用service的其他方法,只是启动一个service的话,那么直接用startService即可,无须bind。
7.对这个模型的感受
这个模型有点囧,作者本来想让码工少写点代码,结果理解起来结构感觉乱糟糟的,而且没有一条主线,内部类各种相互调,客户端服务端不分,太不给力了。除非使用者之前有良好的rpc功底和代码运用能力,否则,要实现自己定制的功能比以前还得小心,要不就是对着示例代码照猫画虎,调试起来就是一种折磨。(一家之言,有理解不对的地方多多提醒,文中示例参考了http://4225953-163-com.javaeye.com/blog/792997一文中的示例)
8.附带标准aidl实现的完整过程:
1)实现要传递的数据结构和aidl(student.java和student.aidl)
2)实现相关的service的aidl(IMyBizService.aidl)
2)如果是eclipse,在1,2完成的情况下,会自动在gen目录下生成IMyBizService.java文件,如果非eclispse,则需要用sdk/platfroms/{version}/tools/aidl工具生成。
3)在myService中集成Stub类,实现IMyBizServic的方法,并作为binder在onBind方法中返回。
4)把IBizInterface.java和Student.java考进客户端工程(aidl文件安不需要,并且包名不能改变),客户端调用Stub的asInterface方法获取到相关的接口代理
5)利用接口代理调用各种方法
6)对于以上各个步骤中的具体操作,网上到处都是。
Android Service和Binder、AIDL的更多相关文章
- Android Service学习之AIDL, Parcelable和远程服务
AIDL的作用 由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象.在Android平台,一个进程通常不能访问另一个进程的内存空 ...
- Android service binder aidl 关系
/********************************************************************************** * Android servic ...
- (转载)Android中的Service:Binder,Messenger,AIDL(2)
前言 前面一篇博文介绍了关于Service的一些基本知识,包括service是什么,怎么创建一个service,创建了一个service之后如何启动它等等.在这一篇博文里有一些需要前一篇铺垫的东西,建 ...
- android service 的各种用法(IPC、AIDL)
http://my.oschina.net/mopidick/blog/132325 最近在学android service,感觉终于把service的各种使用场景和用到的技术整理得比较明白了,受益颇 ...
- Android Service AIDL 远程调用服务 【简单音乐播放实例】
Android Service是分为两种: 本地服务(Local Service): 同一个apk内被调用 远程服务(Remote Service):被另一个apk调用 远程服务需要借助AIDL来完成 ...
- Service组件 总结 + 绑定理Service三种实现方式 Messager + Binder + AIDL
在Android中进程按优先级可以分为五类,优先级从高到低排列: - 前台进程 该进程包含正在与用户进行交互的界面组件,比如一个Activity - 可视进程 该进程中的组件虽然没有和用户交互,但是仍 ...
- Android Service总结06 之AIDL
Android Service总结06 之AIDL 版本 版本说明 发布时间 发布人 V1.0 初始版本 2013-04-03 Skywang 1 AIDL介绍 AIDL,即And ...
- Android studio 中创建AIDL Service
1.概述 AIDL在android系统中的作用 AIDL,Android Interface definition language的缩写,它是一种android内部进程通信接口的描写叙述语言, ...
- 从AIDL开始谈Android进程间Binder通信机制
转自: http://tech.cnnetsec.com/585.html 本文首先概述了Android的进程间通信的Binder机制,然后结合一个AIDL的例子,对Binder机制进行了解析. 概述 ...
随机推荐
- [svc][cpu][jk]cpu的核心查看及什么是cpu的负载
监控的时候我们会监控cpu的负载,那么什么是负载? 编程时候有多核多线程的概念,那么cpu内部如何运作的? 搞清多少bit cpu? 有几个物理cpu?每个cpu是几核的? 之前购买内存条时候,需要关 ...
- Socket网络编程--FTP客户端(2)(Windows)
上一篇FTP客户端讲到如果制作一个简单的FTP客户端,功能实现了,但是后面我们发现了问题,就是FTP是使用明文进行操作的.对于普通情况来说就无所谓了.但有时候要安全的一点的话,就应该使用FTP的安全版 ...
- nginx upstream 常用的几种调度方式
nginx可以根据客户IP进行负载均衡,在upstream里设置ip_hash,以可以对同一个C类地址段的客户端选择同一个后端服务器,除非那个后端服务器宕了才会换一个.C类地址:C类地址第1字节.第2 ...
- text字段增加处理
--text字段增加处理 --创建测试表 ),detail text) insert into test ','A*B' --定义添加的的字符串 ),@postion int select @s_st ...
- 【ARM】AD转换器
A/D转换器 A/D转换器,又称模/数转换器,顾名思义,就是把模拟信号数字化. 由于系统的实际处理对象往往都是一些模拟量(如温度.压力.位移.图像等),要使计算机或数字仪表能识别和处理这些信号,必须首 ...
- Android getWindow().setFlags方法
//设置窗体全屏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams ...
- form的验证包括手机号邮箱等等
$(function(){ var checkedByVerifyCode = false; var checkMobieCode = false; var checkedMobil ...
- 用adb命令组装PowerShell实用小工具——Android测试小助手
[本文出自天外归云的博客园] 简介 APP性能测试一般对以下几个方面进行测试: 1.启动时间(可以通过本工具测试): 2.CPU的占用(可以通过本工具测试): 3.内存的占用(可以通过本工具测试): ...
- Deepin Linux修改Grub引导
grub rescue> 模式修复 登录成功后, sudo upgrade-grub sudo install-grub /dev/sda 系统启动失败,修改fstab, 在grub系统选择界面 ...
- JAVA-数据库之添加记录
相关资料:<21天学通Java Web开发> 添加记录1.使用语句对象Statement的executeUpdate()方法可以很方便地实现添加记录.2.只需要在executUpdae() ...