[gitbook] Android框架分析系列之Android Binder详解

请支持作者原创:

https://mr-cao.gitbooks.io/android/content/android-binder.html


我这是在gitbook 写笔记的时候发现有位博客写的还不错,在征得博主同意后,把它放在csdn上供大家参考,希望大家多多交流


Android Binder详解

本文将对Android的binder机制做一个较为深入的分析,除了讲解binder实现的细节,还会讲解binder通信中的基础原理,以及创建binder service的注意事项。本文的代码分析基于Android4.2.2。

1. binder简介

在我刚刚学习binder的时候,对于binder非常的困惑,现在想起来困惑的原因还是因为对于IPC的不了解。在学习binder之前,最好是对IPC有个基本的了解。IPC是Inter-process communication的缩写,即进程间通信。IPC是一种允许进程间互相通信交换数据的机制。在Linux平台上,进程之间是隔离的,各个进程运行在自己的虚拟地址空间中,如果不采取IPC手段,进程之间是不能互相交换数据的。为了实现进程之间的数据交换,Linux提供了多种IPC机制

  • 信号

  • 管道

  • Socket

  • 消息队列

  • 信号量

  • 共享内存

Android是基于Linux系统开发,除了上面的IPC机制以外,Android又提供了一种新的选择:binder。本文不打算探究这几种机制之间的差异以及优劣,我的主要关注点在binder的实现上。binder的实现采取了面向对象的编程思想,Android提供了大量的帮助类,通过使用这些帮助类,binder程序开发人员基本不用关心数据是如何在进程之间传递的,而是集中精力设计好顶层服务接口,按照规范实现好proxy和service类就可以很方便的扩展一个本地服务。站在binder开发人员的角度来讲,一个binder的实现包括以下三个方面:

  • 顶层服务接口类的定义,此类中声明了一系列的纯虚函数作为公共的服务接口。类的头文件名一般为IXXXService.h,服务接口类命令为IXXXService,XXX为服务模块名。比如Android系统提供的多媒体服务的接口类为:IMediaPlayerService,其头文件名为IMediaPlayerService.h。

  • proxy端的实现。

  • service端的实现。proxy是相对于service而言,proxy和service都间接继承于IXXXService顶层服务接口类,他们都实现了IXXXService中声明的虚函数接口,所以从外观上看,是没什么区别的。对于用户来说,只需要持有一个IXXXService的指针,就可以调用其服务函数,不用关心这个指针究竟是指向哪个子类的具体实现。proxy和service内部对于同一个函数的实现是有差异的,在proxy端的实现中,是将函数的参数打包进容器,然后透过Android提供的binder通信机制传递给service端,service端的实现是从这个容器中读取出对应的参数,然后调用相应的实现函数。从这个角度来说,proxy只是一个空的壳子,它并没有做实际的工作,而是把做实际工作需要的条件打包好,传递给service,由service来完成具体工作。

下面的图简单的描述了顶层服务接口和proxy代理类,与service服务类之间的关系。IXXXService是一个顶层服务接口类,它声明了一个doAction的方法,其子类proxy和service分别实现了这个方法。但是proxy是将doAction方法参数打包,发送给service,由service负责执行。 

binder驱动是通信的媒介,为通信的进程在驱动层分配buffer,将用户层的参数buffer复制到驱动层buffer,完成数据的交换。下图描述了这个过程: 

站在系统角度来说,binder的实现包括:

  • 一个client进程

  • 一个service进程

  • binder驱动

其中,提供服务接口的进程为service进程,使用服务的为client进程。binder在这两者之间充当通信的媒介,所有的通信数据都是经过bidner传递到对方的用户空间。下面是一个简单的图例,描述了一次同步binder调用的过程: 

proxy调用service的服务,称之为一次binder调用。binder调用有两种形式:

  • 同步调用

  • 异步调用

所谓同步调用就是proxy发送完数据给媒介binder driver之后,开始等待的状态。直到service端处理完本次调用,通过binder driver返回了处理结果。异步调用就简单了,proxy直接发送完数据给媒介binder driver之后就返回了,不用等到service的处理结果。

综上,binder是Android提供的一种IPC通信机制,方便进程之间交换数据。binder的实现包括一个公共的顶层服务接口,同时实现了这个公共顶层接口的proxy代理端和service端。binder driver充当通信媒介。

2. binder的实现

上一个小节中,提纲挈领的介绍了bidner的基础信息,本章主要从代码的角度来分析binder的实现。Android给用户提供了一个共享库:libbinder.so,这个库提供了一系列的接口类和帮助函数,借助这些工具类,我们可以很方便的实现自己的binder service。代码的路径:

frameworks/native/libs/binder

首先我们来看一张”全家福“: 

上面这张图中比较清晰的描述了binder库中提供的类之间的关系。在面向对象编程中,一个对象的行为在它的基类中就定义好了,所以我们首先对上图中两个顶层基类IBinder和IInterface做个简单的介绍,以达到纲举目张的目的。

2.1. IBinder类简介

IBinder是负责binder通信机制的基类,它有两个子类——BpBinder和BBinder。BpBinder代表着proxy,而BBinder代表着service。IBinder中有一个函数:

virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);

这是个纯虚函数,它肩负着binder数据传输的重任,从函数的名字就可以看出其重要性,子类Bpbinder和BBinder负责它的具体实现。BpBinder的此函数负责传输数据,而BBinder的此函数是负责接收数据。关于此函数的参数的解释如下:

  • code:函数码值,每一个服务接口类中声明的虚函数都对应一个码值,proxy端通过将码值传递给service端,从而告知service端请求执行的函数。

  • data:Parcel是Android中的容器类,用于装载数据。每个Parcel都对应一块内存buffer,用于存放数据。data中保存的是执行函数所需要的参数,proxy端把数据打包传递到service端,service端按照顺序读取参数,然后传递给对应的函数去执行。

  • reply:指向Parcel对象的指针,和data不同的是,它是用来装载service执行函数后返回的结果。 proxy可以从此Parcel中读取service的返回值,比如service的函数执行完毕之后返回一个int值,那么就可以调用reply->readInt32()获得这个int值。

  • flags:表示函数是同步调用还是异步调用。默认是同步调用,如果需要异步调用,flags会被赋值为IBinder::FLAG_ONEWAY。同步调用是阻塞的,必须等待service执行完毕返回执行结果之后proxy的执行流才得以继续,否则执行函数调用的线程就一直处于wait状态。

IBinder中有这么两个虚函数:

virtual BBinder* localBinder();
virtual BpBinder* remoteBinder();

默认的实现都是返回NULL。在BBinder中实现了localBinder:

BBinder* BBinder::localBinder()
{
    return this;
}

在BpBinder中实现了remoteBinder:

BpBinder* BpBinder::remoteBinder()
{
    return this;
}

所以,如果要区分一个IBinder对象是local binder还是remote binder,那么调用IBinder对象的上述两个函数,对结果进行check就可以知道了。如果localBinder返回非空,那么就是一个local binder,如果remoteBinder返回非空,那么就是一个remote binder。在binder通信中,究竟什么是local binder,什么是remote binder呢?首先,继承自IBinder类的对象,都是binder对象。BBinder因为生存在服务进程中,所以称之为local binder,而BpBinder所对应的实体在另外一个进程中,所以称之为remote binder。BpBinder和BBinder对应关系可以参见下图: 

进程Process_1中有一个BpBinder A,它对应进程Process_2中的BBinder A。相对于Process_1来说,BpBinder A就是remote binder;相对于Process_2来说,BBinder A就是local binder。

同时,进程Process_2中有一个BpBinder B,它对应进程Process_1中的BBinder B。相对于Process_2来说,BpBinder B就是个remote binder,相对于Process_1来说,BBinder B是一个local binder。

从上图我们还可以得知,Process_1调用BpBinder将打包好的函数参数发送到binder driver中Process_2对应的buffer,BBinder A从这块buffer中读取数据,然后进行处理;同理,Process_2调用BpBinder B将打包好的函数参数发送到binder driver中Process_1对应的buffer,BBinder B从这块buffer中读取数据,然后进行处理。一次binder调用从用户空间到kernel空间,数据copy的次数为1。

IBinder类另一个重要的函数:

virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, void* cookie = NULL, uint32_t flags = 0) = 0;

这个函数是用来为BBinder注册死亡通知的。在客户端进程中,持有一个BpBinder,它对应着服务进程中的某个BBinder。如果服务进程crash了,那么BBinder也就不存在了,BpBinder就无法再和BBinder通信了。因为BBinder的死亡客户端是没法主动知道的,所以需要注册个死亡通知:当BBinder不存在了,死亡通知就会被派发,以便客户端进程能做一些善后的工作。这个函数只在BpBinder中实现了——很显然,BBinder不需要为自己注册死亡通知。 DeathRecipient是IBinder的一个内部类,它有一个纯虚函数的方法,需要用户自己去实现:

class DeathRecipient : 
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值