Binder

https://blog.csdn.net/huachao1001/article/details/51504469

https://www.jianshu.com/p/4920c7781afe?from=jiantop.com

什么是Binder

       Binder是Android跨进程通信方式,它实现了IBinder接口,是ServiceManager连接各种Manager(如WindowManager、ActivityManager等)的桥梁。Binder想要实现进程之间的通信,需要解决以下几个问题:

  1. 如何知道客户端需要调用哪个进程以及该进程中的函数
  2. 客户端如何将函数形参发送给远程进程中的函数,以及如何将远程进程函数计算结果返回客户端
  3. 如何去屏蔽底层通信细节,让实现客户端调用远程函数就像调用本地函数一样

第一个问题,很容易解决,只要给每个需要远程通信的类唯一标识就可以通过包名+类名的字符串就可以做到,然后在类里面给每个函数编号即可对函数唯一编码。第二个问题,定义一个可打包的接口Parcelable,这个接口提供2个重要函数,分别是将对象中的属性写入到数组和从数组中的数据还原对象,每个可以发送到远程函数作为形参的对象只需实现Parcelable对象即可。第三个问题,为了屏蔽进程之间的通信细节,定义一个Binder类,这个类得帮用户发送远程请求并将拿到返回结果提交给用户。其次,如果我想实现服务端知道客户端什么时候调用我。本文后面所指的Binder类都是指远程服务端的对象。服务端想要实现被跨进程访问,就必须继承Binder类。 

首先我们看看我们的程序跨进程调用系统服务的简单示例,实现浮动窗口部分代码:

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

系统服务都是运行在systemServer进程中,因此我们调用系统服务都是跨进程的调用。第2行代码中,得到的wm是WindowManager对象的引用,第6行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。是不是很想知道addView发生了什么?我们先看看Binder机制吧!看完Binder原理,再解释!

代码执行过程

假设你已经创建好服务端类MyService、客户端类MyClient。在客户端持有MyService的引用,并且调用了MyService的func函数,那么Android内部调用过程如下:

MyClientMyClientBinder驱动Binder驱动MyServiceMyService请求相关数据:Parcel客户端当前线程被挂起从线程池中取出线程并执行客户端指定的函数等待执行结果执行函数func计算结果返回得到返回结果唤醒被挂起的线程并将返回结果传给客户端当前线程被唤醒,取出返回结果,继续往下执行。

客户端当前线程会被挂起!因此,如果远程进程是执行长时间的运算,请不要使用主线程去调用远程函数,以防止ANR。

Binder的C/S架构

在Android开发中,我们大量使用到了系统Service,比如媒体播放、各种传感器以及WindowManagerService等。那么Android是怎么管理这些服务,并且让用户跨进程调用这些服务呢?首先我们看看调用系统服务的过程。在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,然后通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。

客户端调用系统服务过程

Binder驱动实现原理

客户端持有远程进程的某个对象引用,然后调用引用类中的函数,远程进程的函数就执行了。不同的进程之间是不共享资源的。也就是说,客户端持有的这个对象跟远程进程中的实际对象完全是两个不同的对象。客户端调用引用的对象跟远程进程半毛钱关系都没有,凭啥远程进程就调用了执行了?

Binder驱动实现原理
服务端跨进程的类都要继承Binder类。我们所持有的Binder引用(即服务端的类引用)并不是实际真实的远程Binder对象,我们的引用在Binder驱动里还要做一次映射。也就是说,设备驱动根据我们的引用对象找到对应的远程进程。客户端要调用远程对象函数时,只需把数据写入到Parcel,在调用所持有的Binder引用的transact()函数,transact函数执行过程中会把参数、标识符(标记远程对象及其函数)等数据放入到Client的共享内存,Binder驱动从Client的共享内存中读取数据,根据这些数据找到对应的远程进程的共享内存,把数据拷贝到远程进程的共享内存中,并通知远程进程执行onTransact()函数,这个函数也是属于Binder类。远程进程Binder对象执行完成后,将得到的写入自己的共享内存中,Binder驱动再将远程进程的共享内存数据拷贝到客户端的共享内存,并唤醒客户端线程。

Binder机制运用

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注册服务(addService):在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。这一步是系统自动完成的。

获取服务(getService):客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,通常是Service引用的代理对象,对数据进行一些处理操作。即第2行代码中,得到的wm是WindowManager对象的引用。

使用服务:通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即第6行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。

使用服务的具体执行过程

  1. client通过获得一个server的代理接口,对server进行调用。
  2. 代理接口中定义的方法与server中定义的方法是一一对应的。
  3. client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成Parcel对象。
  4. 代理接口将Parcel发送给内核中的binder driver。
  5. server会读取binder driver中的请求数据,如果是发送给自己的,解包Parcel对象,处理并将结果返回。
  6. 整个的调用过程是一个同步过程,在server处理的时候,client会block住。因此client调用过程不应在主线程。

这段代码前面已经出现过。getSystemService(getApplication().WINDOW_SERVICE);函数内部原理就是向ServiceManager查询标识符为getApplication().WINDOW_SERVICE的远程对象的引用。即WindowManager对象的引用,这个引用的真正实现是WindowManager的某个代理。得到这个引用后,在调用addView时,真正的实现是在代理里面,代理把参数打包到Parcel对象中,然后调用transact函数(该函数继承自Binder),再触发Binder驱动的一系列调用过程。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值