




已阅读5页,还剩18页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
初探Android中的binder机制Binder机制是Android系统中最主要的进程间通信机制。虽然Android底层使用的是linux内核,但是除了匿名管道,socket外,Android并没有使用linux中的命名管道,信号量,消息队列等传统的IPC通信方式。Binder犹如一张大网,将Android整个系统中的组件,跨进程的组织在一起。淡化进程,强化组件是Android的一个设计理念。在这个过程中,Binder发挥了巨大作用。binder 的作用binder的作用可以概括为两点:IPC:是一种跨进程通信手段,但是传输数据的大小受限制,不建议传输较大数据。RPC:是一种远程过程调用手段,即一个进程中可以透明的调用另外一个进程中的方法。两者经常相互伴随,例如跨进程调用的时候,参数的传递以及结果的传递等。binder机制简述binder实体对象: 就是binder服务的提供者,一个提供binder服务的类必须继承BBinder类(native层),因此binder实体对象又叫做BBinder对象。binder引用对象: 是binder服务提供者在客户端进程的代表,每个引用对象类型必须继承BpBinder类(native层),因此binder引用对象有叫做BpBinder对象。binder代理对象: 代理对象简单理解为内聚了一个binder引用对象,因此可以通过这个内聚的引用对象发起RPC调用。可以有多个不同的代理对象,但却内聚了同一个引用对象。IBinder对象: BBinder和BpBinder都是继承自IBinder.因此binder实体对象和binder引用对象都可以称为IBinder对象。可以通过IBinder.queryLocalInterface()方法来判断到底是binder实体对象还是binder引用对象。binder跨进程传输的数据类型是Parcel。以RPC为例的示意图如下:android_binder-1.png通过一个运行在内核空间的binder驱动进程,将两个用户空间的进程联系了起来。为解决由于用户空间进程之间虚拟地址相互独立而引起的无法跨进程调用别的进程中的对象方法的难题带来了曙光。通过BInder引用对象发起RPC调用假设App中的MainActivity中以startActivityForResult()方法启动了该App中的Main2Activity,那么Main2Activity可以通过AMS这个binder服务中的getCallingActivity()方法查询是谁启动了自己。AMS.getCallingActivity():public ComponentName getCallingActivity(IBinder token)去参数是Activity.mToken. 可以通过反射从客户端Activity组件中获取。1). 获得AMS的引用binderAMS作为一个系统服务,在Android系统启动过程中,会将自己注册到ServiceManager中(注册过程以后在分析)。现在只要知道客户端可以通过ServiceManager来获得AMS的引用binder即可。2). 使用引用binder进行RPC调用时,需要直到要调用的方法的编号,这个编号可以从Android源码中获取。在Android 6.0 中该方法编号如下: int GET_CALLING_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+21;服务端根据这个编号,执行相应的逻辑。3). 方法参数的传递binder中唯一能的传递的数据结构就是Parcel,所以必须将方法的参数打包到Parcel中。针对不同的数据类型Parcle提供了不同的方法,来将这些对应的数据打入Parcel中。另外还要准备一个Parcel用于接收服务端的返回数据。4). 发起RPC调用通过引用对象的transact()方法,发起RPC调用,绝大多数情况下,此调用是一个同步调用,也就是说会一直阻塞到服务端将数据返回为止。但是当transact()方法中传入的flag为FLAG_ONEWAY时,方法会立即返回,不会等到服务端返回数据。该方法会最终将上述信息传入binder驱动中去。5). 客户端从返回的Parcel中读取数据服务端中将请求的方法执行完毕之后,将方法返回值打入到parcel中,binder驱动将其返回到客户端组件中,然后客户端组件按照调用方法的返回值类型,从返回的parcel中读取即可。public class Main2Activity extends AppCompatActivity Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); / 拿到AMS的引用对象 IBinder sm = (IBinder) Reflect.on(android.os.ServiceManager).call(getService,activity).get(); / 拿到方法编号 int funCode = IBinder.FIRST_CALL_TRANSACTION+21; / 准备方法参数数据 Parcel data = Parcel.obtain(); / 首先要写入的数据必须是binder服务端的descriptor data.writeInterfaceToken(android.app.IActivityManager); / 接下来是方法的参数 data.writeStrongBinder(IBinder)Reflect.on(this).field(mToken).get(); / 用于接受返回数据 Parcel reply = Parcel.obtain(); / 发起RPC调用,同步调用,直到调用结束,期间一直阻塞 try sm.transact(funCode,data,reply,0); catch (RemoteException e) e.printStackTrace(); / 读取返回数据 reply.readException(); / 解析返回数据 ComponentName res = ComponentName.readFromParcel(reply); / 回收parcle data.recycle(); reply.recycle(); Log.i(shajia,calling Activity name: +res.getClassName(); 通过binder代理对象发起RPC操作对于前面例子中获取调用者的情况,实际开发中都是通过Activiy.getCallingActivity()来获取的:public ComponentName getCallingActivity() try return ActivityManagerNative.getDefault().getCallingActivity(mToken); catch (RemoteException e) return null; 其中ActivityManagerNative.getDefault()返回的是ActivityManagerProxy对象。ActivityManagerProxy是AMS的binder代理类。binder代理对象通过内聚的binder引用对象间接发起RPC操作。对于系统服务来说,它的binder代理对象都是事先定义好的。binder代理对象还要实现服务接口,实际上就是对binder引用对象发起RPC操作的二次封装。class ActivityManagerProxy implements IActivityManager.public ComponentName getCallingActivity(IBinder token) throws RemoteException Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); mRemote.transact(GET_CALLING_ACTIVITY_TRANSACTION, data, reply, 0); reply.readException(); ComponentName res = ComponentName.readFromParcel(reply); data.recycle(); reply.recycle(); return res;.可以看到代理类中已经帮我们封装好了getCallingActivity()的操作。因为直接通过binder引用发起RPC操作的话,需要开发者知道方法的编号,而方法的编号又是随着Android版本的变化而可能发生改变的。所以一般来说都会为binder服务封装一个binder代理类。这样做还有一个好处是通过一个binder引用对象,可以创建多个binder代理对象。binder服务分类分为两大类:向ServiceManager注册的binder服务Android系统中自带的绝大多数服务,例如AMS,PMS等都会向ServiceManager注册,注册时会传入一个service名字,例如AMS注册是传入的是“activity”。客户端可以通过名字向ServiceManager查询对象的binder服务,ServiceManager会返回一个IBinder对象。至于说返回的IBinder对象究竟是实体bidner呢还是引用binder,按照下面的规则决定:当binder服务端同进程请求该服务时,返回的是binder实体对象。当请求者与binder服务端在一个进程时,返回的是引用对象。没有向ServiceManager注册的的bidner服务,又被称为匿名binder服务典型代表就是App中通过aidl实现的service组件。aidl实际上帮我们完整实现了服务代理类,以及是是实现了binder服务类中与语义处理相关的所有操作,例如方法编号的分配,方法参数从parcel中的提取,以及返回值打入parcel中等所有的操作。开发者只需要实现服务接口即可。因为app是没有权限向ServiceManager注册服务的,那么怎么获取app中的额service实体的引用binder呢?那就是直接传递binder实体,将binder实体对象打入到Parcel中,跨进程传入到AMS中去。Binder实体在Binder驱动中的传输,会被特殊处理,最终返回到AMS中的是一个binder引用对象。(详细过程后续在分析喽!)其他App进程中的组件便可以通过AMS拿到要请求的service服务的binder引用对象了。要注意的是,此过程中是AMS将所请求的binder服务的引用对象打入Parcel,然后通过Binder驱动传递到请求者进程中。简单的说就是Binder实体对象的传递过程,伴随着binder服务在Binder驱动中相关数据结构初始化以及binder引用对象的创建过程。不通过aidl实现一个service,来熟悉一下整个过程:1. 首先顶定义一个服务接口,即服务要对外提供哪些方法。IBinderService.java:public interface IBinderService extends IInterface String getMessage(); /* * 服务的描述,客户端在使用parcel跨进程传输数据的时候 * 必须首先写入服务的描述,即该数据是发给哪个binder的。 * 将来服务端收到数据后会检查这个服务描述是否和自己的一致,不一致就不做处理了 */ String DESCRIPITON = MyBinderService; / 定义方法编号 int GET_MESSAGE = IBinder.FIRST_CALL_TRANSACTION+0;binder服务接口必须继承自IInterface接口,该接口中只有一个方法:public IBinder asBinder()binder服务接口一般要包含三部分内容:首先是该binder服务的描述DESCRIPITON,发送数据时必须先发送该描述,接收数据时必须先对该描述进行检查,和自己不匹配的话那就不用继续进行了。然后是方法编号,这个编号实际含义要在binder实体对象中的onTransact()方法中才能体现出来。可以理解为onTransact()根据这个编号调用不同的处理分支。最后就是该服务对外提供的具体方法声明了。binder实体端和代理对象端必须都继承这个服务接口,并实现其中的方法。另外服务端还要重载binder的onTransact()方法。2. 实现bidner服务端binder服务端的重点在于实现onTransact()方法,该方法中会依据客户端传入的方法编号,调用恰当的分支进行处理。处理过程也是很简单的,就是从parcel中解析参数,调用对应的方法执行,将执行结果打入parcel中。public class BinderServiceStub extends Binder implements IBinderService public BinderServiceStub() / 调用该方法后binder实体端的binder.queryLocalInterface() / 返回就不会为null。 attachInterface(this,DESCRIPITON); / 实现服务接口方法 public String getMessage() return i am from Message!; /* * 顾名思义,将自身转换为一个IBinder对象, * 因为Binder继承子Binder,Binder继承自IBinder */ Override public IBinder asBinder() return this; Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException switch (code) case GET_MESSAGE: / 客户端先发送的是服务描述,所以这里先接收服务描述并判断是否和自己一致 data.enforceInterface(DESCRIPITON); / 开始执行客户端请求的服务端的方法 String msg = getMessage(); / 将结果打入Parcel reply.writeNoException(); reply.writeString(msg); return true; /* * 必须调用父类onTransact处理其他code */ return super.onTransact(code, data, reply, flags); 实现binder代理端前面介绍了,binder代理对象类会内聚一个binder引用对象,该引用对象通过构造方法传入即可:public class BinderServiceProxy implements IBinderService /* * 内聚的binder引用对象 */ private IBinder remote; public BinderServiceProxy(IBinder binder) if(binder.queryLocalInterface(DESCRIPITON) = null ) remote = binder; else throw new RuntimeException( this is not a BpBinder.); Override public String getMessage() Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(DESCRIPITON); try remote.transact(GET_MESSAGE,data,reply,0); catch (RemoteException e) e.printStackTrace(); reply.readException(); String msg = reply.readString(); data.recycle(); data.recycle(); return msg; Override public IBinder asBinder() return null; 这里要特别注意的是,因为binder代理对象类内聚的是一个binder引用对象,所以要对构造方法中传入的Ibinder对象进行检查,保证其是binder引用对象。然后就是实现服务接口方法,这里很简单了,就是组装Parcel数据,然后利用引用binder对象发起RPC调用。4. 获取binder引用对象这就要借助Android中的service组件了,并且以bindService()方法启动该service。首先定义service组件:public class MyBinderService extends Service Nullable Override public IBinder onBind(Intent intent) return new BinderServiceStub(); 然后在清单文件中,声明该service组件,并且设置process属性,保证该service运行在另外一个进程中:最后以bindService()方式绑定该service:Intent intent = new Intent(); intent.setClass(this,MyBinderService.class); bindService(intent, new ServiceConnection() Override public void onServiceConnected(ComponentName name, IBinder service) if(service.queryLocalI
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度航空航天卫星通信设备采购与技术支持协议
- 2025年绿色交通新能源汽车推广与应用合作协议
- 2025年度生物材料研发数据保密及授权使用合同
- CNG运输管道HSE环保设施改造与提升技术服务合同
- 2025年农业节水滴灌系统研发与国内市场推广合作协议
- 2025年绿色住宅区绿化养护与生态修复工程合同
- 2025年糖业转型升级与品牌塑造战略合作框架协议
- 2025年音乐MV主演角色选拔与聘用合同
- 2025年绿色环保型跨境电子商务出口合同范本
- 2025年度红茶品牌授权生产与销售合作协议
- 基本药物临床应用管理制度
- 放射科新技术介绍
- 盆底功能障碍问卷(PFDI20)
- 居住证申请表(正式版)
- 护士临床思维建立
- 公共场所卫生知识培训材料
- 证据目录范本
- 标准档案盒脊背(格式已设置好)
- GB/T 21475-2008造船指示灯颜色
- 园林绿化工高级技师知识考试题库(附含答案)
- 安医大生殖医学课件04胚胎的培养
评论
0/150
提交评论