应用层开发者想要在Android端更上一层,Android基础知识、启动过程、编译打包apk过程、安卓framework层等实现原理都是一定要掌握的,熟悉安卓四大组件的深层通信过程及原理和Android构建工具gradle的实现原理也都逐渐成为必要技能,这就是好比步入应用层中高级工程师的一道门,这道门对于应用层初学者来说却无比坚实。
本篇旨在为我们学习Android运行机制奠定基础,因为包括启动流程(了解之后可以做启动优化)、四大组件的交互、Hanlder都会关联到。通过这些学习,你的应用可以实现最直观的启动优化。例如:很多应用后续使用过程,除了默认进程外会再启动一些服务类进程(有三方的类似推送,也有可能是我们自己创建的其他服务类进程),当应用从launch桌面启动(AS性能检测可以看到,或者自己debug),系统创建应用默认进程(与applicationId保持一致),然后在应用进程中做一些必要的初始化工作,大部分需要初始化的工作,都是默认进程所必须的,非默认进程只需要针对自身做必要的部分初始化工作即可。也就是说,当我们做好所有的单进程(ApplicationId同名进程)初始化等工作,安全又快,也就实现了应用从最初的4-5s左右到后来的2-3s,纯原生鱼雷基本不到2s(自动登陆接口响应需要1s多),如果再配合一些其他的优化手段,画面太好。你觉得提升2s没啥用?那是没见过做启动页开屏广告十几家广告一起来,至于显示我们不关心,但是你肯定是都要初始化的,要是代码比较任性,超10s那也常见(初始化必要工作太多的不在讨论范围),正常到3s这个差别还是很大的。本来只在默认进行做的工作,你每个进程都做;而且多进程和多线程一样,操作数据也要考虑到安全性。下面正式开始对Binder通信机制和过程涉及的相关内容进行阐述(如本文有理解误区,万望读者不吝赐教):
(一)进程间通信有多种方式,这里主要介绍Android进程间通信Binder特点及实现过程:
在 Android 中,一个进程通常无法访问另一个进程的内存。为了进行通信,需将其对象分解成可供操作系统理解的原语,并将其编组为可供开发人员操作的对象。如果开发人员直接编写执行该操作的代码较为繁琐,因此Android引入AIDL实现进程通信。aidl(Android Interface Definition Language)接口定义语言,可以定义客户端与服务均认可的编程接口,实现进程通信。学习Binder前,强烈建议先了解aidl的实现过程,通过学习aidl的工作过程,我们会对进程通信的流程有大致的概念,有利于底层执行的命令、调用接口和执行上层写入/读取相对应的理解,后面学习底层的过程就好比验证每个过程的更深层实现。比如:C/S端通信transact/onTransact方法内实现数据交互,消息源筛选、数据类型及数据的读写,特点一目了然;鉴于篇幅,有关aidl的实现过程请自行学习,尽量熟练掌握(可以学习Android开发艺术探索和张鸿洋的博客)。
特点:安全、传输性能高和操作相对简单;安全性,对比socket/管道通信方式,Binder通信过程中,系统每创建一个进程会分配对应的PID和UID(此特点还可以用于多进程项目的某些方向区分优化),进程之间通过进程id匹配确认消息来源;传输性能高,传输数据不需要tcp协议的三次握手,传输过程只需一次拷贝,操作简单;共享内存虽然不需要拷贝,但实现起来要复杂很多。
(二)了解下内存映射函数mmap和读写数据接口ioctl:
mmap用于建立用户空间和内核空间的映射关系,实现映射关系内存空间共享数据;建立映射关系的不同内存空间的数据操作,会映射到建立关系的其他内存空间,对应关系如下图:
ioctl是底层和应用层之间定义的接口协议(用于操作写入/读取数据),不提供单独的read/write接口,一次调用实现写入读取数据,满足数据交互同步。假设需要进行写入/读取数据(write_size和read_size都大于0),在操作写入和读取的过程,先将write_buffer里面的数据写入Binder;然后同步等待数据返回,从Binder中读取数据存入read_buffer。因为BINDER_WRITE_READ命令包含写入和读取数据两步操作,且是否进行写入和读取操作都是根据Binder的write_size和read_size确定的。假如只写入,设置Binder的write_size大于0,read_size等于0即可;其他情况同理。
(三)接下来对进程通信C/S端采用Binder通信过程的write/read及数据传递进行介绍:
在aidl操作的过程,有单独的write和read方法,在底层命令是一次执行,写入/读取根据顺序判断是否需要执行(write_size/read_size)对应操作。比如Server/Client端执行write/read完成后,进入等待接收数据状态,接收到数据之后做相应操作并回应reply。
Binder写入/读取的数据结构体是binder_transaction_data,该数据体包含了上述,比如:进程id、用户id用于接收方确认发送方,data_size缓存区存放的数据长度(由发送方设置,用于接收方确定接收到的数据大小)。
系统为每个应用分配一定的内存(虽然应用可以自行配置,但不能超过系统可分配的最大值),且每次处理完之后会执行BUFFER_FREE命令释放内存。上面我们提到mmap()映射函数,在Binder通信的写入/读取过程中,采用的是接收数据缓存的动态分配和释放。程序处理完某缓存区数据之后,底层会调用命令释放缓存区,否则会导致缓存区耗尽而无法接收数据。
关于Binder的C/S端通信,总结性描述:Client端将函数参数打包,在transact()中通过服务端Binder向Server发送数据包请求/等待数据返回,Server端在onTransact()中通过客户端Binder获取binder_transaction_data数据,取出进程id及其他数据做相应处理,应答Client端。很多讲解这块儿内容的时候,都提到用对方Client/Server的Binder进行数据操作,实现数据交互。在底层本质上是,进程通过ServiceManager(简称SMgr下面会详细解释)注册,用的时候直接在注册表查找,就可以实现两端都持有彼此的引用(可以暂时理解为对象的引用遍布各处),由于应用层实现不需要修改底层代码,所以这也是便于理解的一种说法。在Client和Server通信的过程,SMgr类似于域名服务器,当客户端向服务端发起请求的时候,通过SMgr找到初始进程对应Binder引用(类比在域名服务器中找到域名对应的主机)进行写入/读取;SMgr进程的转化及其Binder创建,如下图:
通信主要由四部分组成:Server、Client、SMgr和Binder驱动;前两个是需要实现通信的两端,驱动负责把Server端Binder相关信息通过数据包形式发送到SMgr,SMgr收到数据包之后解析数据注册Server端的Binder引用,后续SMgr还会负责在Binder的登记表里查找每个进程对应的Binder引用。大致过程为:Server端创建Binder实体之后,通过驱动以transaction_data数据包的形式发送到SMgr,通过SMgr将对应引用注册到内核Binder注册表。以上发送及注册引用的具体过程为:Binder实体创建之后,驱动会在内核创建其对应的实体节点和用于传递给SMgr的Binder引用,然后把实体信息和刚刚创建的Binder内核引用等信息传递给SMgr,SMgr进程解析数据将引用插入Binder注册表,Client端就可以在表中查找该引用执行write_buffer/read_buffer等操作;其他进程发送数据,SMgr接收数据并执行注册过程,如下图:
实际上要完成上面的过程,首先Server和SMgr也涉及到进程间通信,SMgr可以理解成系统默认进程,其他进程和SMgr通信的时候,SMgr都是作为Server端;那么SMgr和Server端通信时,SMgr中的Binder引用到底是谁传过来的,都要它注册,那他自己的Binder谁注册?比方SMgr进程是项目经理,其他进程是项目组织成员,项目经理给每个人的权力(Binder)去完成工作,那么项目经理分配权限,项目经理的权力哪里来?肯定需要管理层赋予他职责。接下来解释下SMgr的Binder引用哪里来,当某个进程执行BINDER_SET_CONTEXT_MGR命令将自身注册成SMgr进程时,驱动会为该进程创建好Binder实体(0号引用),并且其他所有进程都能获取到该引用,系统只能有一个SMgr进程。后续任何进程向SMgr注册Binder时,都要通过该引用(SMgr的Binder实体引用)和SMgr进程进行通信。进程注册及进程间通信整个过程,大致理解为SMgr作为主导注册/查找对应Binder对象进行写入/读取,如下图:
所以,进程间通信就可以分为:SMgr和其他进程,Server、Client和SMgr两种。本质上都是用对应进程的Binder对象实现写入/读取等操作。区别在于进程对应的Binder对象:前者系统注册的时候就会定为0号引用;后者需要SMgr(Binder对象管理中心)实现注册,后续在注册表中根据定义的key取出对应进程的Binder引用执行操作。
此处,我们延伸一下通信过程中线程的交互(后面写关于Handler的内容会更具体解释),如下图:
本文参考资料:
1. https://blog.csdn.net/augfun/article/details/82343249
2. https://baike.baidu.com/item/mmap/1322217?fr=aladdin
3. https://baike.baidu.com/item/ioctl/6392403?fr=aladdin
4. https://developer.android.google.cn/guide/components/aidl.html?hl=zh-cn
5. http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0319/2619.html
6. https://www.zhihu.com/question/20122137/answer/14049112