您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
Android Binder机制介绍
 
来源:麦穗 发布于: 2016-8-1
  1964  次浏览      15
 

做过Android开发的同学可能有些体会,入门初期,工作内容主要是实现各式各样的UI界面,以及实现应用的业务逻辑。在这个阶段,我们会逐渐熟悉View系统,逐渐学会实现各种各样的界面以及动画效果。再往后,当我们想更深入的学习android系统,比如学习android四大组件的启动过程、AMS、PMS等等时,都会遇到一个叫做Binder的东西。结合笔者的经验,Binder可以说是深入理解Android系统的重要基础。binder作为android系统进程间通信的机制,贯穿在方方面面。我们平时使用最多的startActivity、startService都是通过binder机制与AMS所在进程进行通信。本文主要对Binder机制的体系结构作简要介绍,相信读者看完后,会对binder有一个总体上的理解与把握。

注:笔者初学binder时,曾经看过一些binder介绍的文章,并且过早的纠结于一些文章中的binder代码细节,感觉非常吃力。本文仅从宏观上对binder机制进行介绍。相信读者先理解了binder的总体结构后,再去深入细节,学习效果会更好。

Binder是什么、能做什么?

Binder是android系统里面的进程间通信机制。androd系统中,不同的app运行在不同的进程中,同一个app的不同组件也可能运行在不同的进程中(androidManifest文件中android:process)。当一个进程想为其它进程提供服务时,就需要通过进程间通信的方式来提供服务。打个比方:我们有一个APP1,里面有个service组件可以提供计算器的服务。当另外一个APP2也想使用APP1里面的service的计算器的服务时,由于不同的APP运行在不同的进程中,所以,APP2是无法直接使用APP1里面的service。由于跨越了进程,只能通过进程间通信机制来完成。

再说的形象点,APP1所在进程有一个对象object1,其中有一个方法method1。 APP2所在的另外一个进程,想使用object1的method1方法。binder可能帮助我们在APP2所在进程拿到一个object1对象的引用,使我们能够像调用本地对象一样,通过object1.method1()直接调用。利用binder,我们可以突破进程的限制,将对象传给其它进程,让其它进程方便调用对象的方法。

为什么用Binder?

理解了binder是什么、能做什么后,大家可能会有疑问:android系统基于Linux,linux本身具有很多的进程间通信方式可供选择,为什么android不使用linux自带的一个进程间通信方式,而新创造了一个binder?是重复造轮子吗?

Linux自带的进程间通信方式有:文件、signal、socket、Pipe、共享内存…, 为什么使用Binder?笔者总结原因有两大方面:

1 历史原因。Binder最早并不是为Android系统而设计的,最开始有一个OpenBinder的东西,用在一个叫做Palm Cobaltw的为内核操作系统上。后来,Palm Cobaltw移植到了Linux系统上,OpenBinder也跟着移植了过来。Google在组建Android开发团队的时候,聘请了一位叫做Dianne Hackborn的工程师,而他就是OpenBinder的核心人员。后面在做android进程间通信时,发现binder很合适,就理所当然的在android系统上使用了Binder。

2 binder自身的一些特点和优势。Binder实现进程间通信时,在安全性和效率方面,都很合适用在android系统中。关于这点,先有个印象即可。

Binder有哪些组成部分?

一个Binder系统由四部分组成:Binder客户端、Binder服务端、Binder驱动、服务登记查询模块

Binder客户端:想要使用服务的进程

Binder服务端:实际提供服务的进程

Binder驱动:我们在客户端先通过Binder拿到一个服务端进程中的一个对象的引用,通过这个引用,直接调用对象的方法获取结果。在这个引用对象执行方法时,它是先将方法调用的请求传给binder驱动;然后binder驱动再将请求传给服务端进程;服务端进程收到请求后,调用服务端“真正”的对象来执行所调用的方法;得出结果后,将结果发给binder驱动;binder驱动再将结果发给我们的客户端;最终,我们在客户端进程的调用就有了返回值。Binder驱动,相当于一个中转者的角色。通过这个中转者的帮忙,我们就可以调用其它进程中的对象。

服务登记查询模块:我们调用其它进程里面的对象时,首先要获取这个对象。这个对象其实代表了另外一个进程能给我们提供什么样的服务(再直接一点,就是:对象中有哪些方法可以让客户端进程调用)。首先服务端进程要在某个地方注册登记一下,告诉系统我有个对象可以公开给其它进程来提供服务。当客户端进程需要这个服务时,就去这个登记的地方通过查询来找到这个对象。

Binder各部分是如何工作的?

下面从客户端进程的角度来看看binder的工作机制

作为客户端进程,仅仅是想使用服务端进程提供的服务而已:

但是由于进程的隔离性,Client所在的ProcessA是不能读写Service所在的ProcessB中的内容,但系统内核可以,而Binder驱动就是运行在内核态。Binder驱动帮我们中转请求即可:

有了Binder驱动后,对于Client端和Service端,需要额外专门与Binder驱动打交道,为了帮Client和Service端屏蔽掉与Binder驱动打交道这件很low的工作,我们可以分别为Client和Service设计一个代理,让他们只与代理打交道即可,将与Binder驱动打交道的任务放在代理里面:

读者可能要问,如果要自己实现Client端和Service端,通过上面的方式,虽然逻辑上独立出来了两个代理(客户端代理的Proxy、服务端的代理Stub),这两个代理还不是要自己实现?如果还是要自己实现,那分出来又有实际的意义?

AndroidSDK为我们提供了一个AIDL工具。我们只要新建一个AIDL文件,像创建普通interface一样(略有不同)在里面定义接口方法。定义好之后,androidSDK会根据aidl里面的接口定义,自动为我们生成一个java文件,其中就包括客户端代理Proxy和服务端代理Stub,并且自动生成了与Binder驱动通信的代码,是不是很爽。上面的图片中,之所以将客户端代理叫做Proxy,服务端代理叫做Stub,就是因为aidl自动生成的代理中,两个代理的类名就是Proxy和Stub。我们在做的实际工作就只有真正的服务功能逻辑了。

再更近一步的想想,对于客户端而言,更理想的状态是,客户端完全不用知道调用的对象是一个本地对象,还是一个服务端进程中的对象。按照上面的图片,客户端显然知道自己调用的是远程对象,所以通过一个Proxy来调。我们在开发android应用程序的过程中,都会用到一些系统提供的XXXManager(ActivityManager、PackageManager、WifiManager、PowerManager等等),而这些manager所要实现的目的之一,正是帮Client屏蔽掉Binder的实现细节。有了这些Manager,Client在使用时,首先通过getServiceManager(xxx)获取一个manager对象,后面就直接调用manager中封装好的服务方法即可。这样以来,manager内部实际上还是通过客户端代理,通过binder驱动来跟运行在另外一个进程中的xxxService来通信,但对于Client来说,完全不用知道。如下图:

对于一些系统服务,ActivityManager、PackagerManager等等,不光为Client屏幕了Binder相关细节,还可以对真正的服务端对象提供的服务API进行过滤和控制,可以只为Client暴露一个服务API的子集。

最后一个疑问:到上面的图片所讲的内容为止,前提条件是Client先获取到了一个Proxy或者Manager,然后才完成后面的进程间通信。那到底Client是如何获取到Manager或者Proxy呢?

首先以一些系统服务为例:

前面提到,Binder机制的四个组成部分中,有一个是“服务登记查询模块”,对应上图中的Context Manager,在Android系统中运行时的进程名为:servicemanager。在所有服务进程中,servicemanager第一个启动,原因也不难理解,如果其它服务进程先于servicemanager启动,到哪里去注册?

如上图所示,假设Service所在的进程ProcessB在servicemanager进程启动后启动,ProcessB需要向servicemanager注册。“注册”这个动作,本质也是要跨进程通信,从ProcessB到servicemanager,这时,Service成为了本次跨进程通信的客户端,ContextManager成为了服务端。客户端到服务端的通信,按前面讲到的内容,需要一个Proxy或者Manager。这个Proxy或者Manager从哪里获取呢?问题好像陷入了循环死锁?因为ContextManager的特殊地位,读者可以理解为系统对它有点特殊待遇,Service可以从一个全局固定的地方直接去拿Proxy或者Manager,而不用像Client那样通过查询来获取Proxy/Manager。拿到了客户端代理,具体注册的通信过程如上文所讲,不再重复。到这里为止,Service就成功注册到了ContextManager中。

客户端Client初始化Manager时,manager也像刚才的Service,直接从某个固定的地方拿到ContextManager的代理,通过代理,去查询ContextManager,获取一个“提供所要服务的代理对象”。manager有了这个对象,Client就可以通过manager来使用服务了,调用逻辑如图:

当自己来实现Client和Service时,Client如何获取Proxy代理对象?

先讲一下实现方式:一般我们会在App中通过Service组件的方式来创建一个服务。其它App通过BindService的方式连接到Service,通过onServiceConnected回调就能获取Proxy代理对象。具体实现可参考:http://blog.csdn.net/singwhatiwanna/article/details/17041691

总结

本文简要介绍了Binder跨进程通信机制的逻辑,希望能帮助初学Binder的同学快速入门,提交学习效率。

   
1964 次浏览       15
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...