binder核心原理解析

在一些中小型app中主动使用多进程的几率较低,可是也不表明小型app只有一个进程,好比集成了推送通常第三方就是重开的进程,只是咱们没有注意到而已。可是大型app中多进程是很是常见的,就以微信为例,咱们能够查看一下微信的进程,发现有不少个,那么既然多进程是一个较复杂的概念,为何微信会使用这么多进程呢?
java



1、为何要使用多进程?

一、突破虚拟机分配进程的运行内存限制;android

      在Android中,虚拟机分配给各个进程的运行内存是有限制值的(根据设备:32M,48M,64M等)随着项目不断增大,app在运行时内存消耗也在不断增长,甚至系统bug致使的内存泄漏,最终结果就是OOM。编程

二、提升各个进程的稳定性,单一进程崩溃后不影响整个程序;小程序

      小程序进程崩溃,不影响其余进程,不会致使闪退安全

三、对内存更可控,经过主动释放进程,减少系统压力,提升系统的流畅性;微信

      在接收到系统发出的 trimMemory(int level) 中主动释放重要级低的进程app


2、如何使用多进程?

使用多进程很简单,只须要清单文件中使用process进行标注便可
源码分析


3、多进程如何通讯?

binder是Android跨进程的底层机制,跨线程之因此不推荐Socket、管道(pipe)来实现,是由于binder的性能更好,安全性更高。固然也有广播、ContentProvicer也有各自的应用场景,只是不推荐使用,好比广播有溯源难的问题。性能

跨进程通讯能够看作是CS模式,客户端和服务端的通讯。A进程调用B进程的方法获取返回值,或者是修改其属性。


咱们知道,在进程A中不能直接调用到B中的方法,由于不一样进程是加载在不一样的内存中,不能直接进行通讯。内存分为用户空间和内核空间,用户空间供咱们使用,内核空间是公用。咱们app中的数据都是保存在用户空间,想要实现进程中通讯,就必须将数据从用户空间拷贝到内核空间,而后再从内核空间拷贝到用户空间,这样进行交互。因此就必须依靠内核空间进行通讯,其余的通讯方式好比管道也是如此,可是binder是经过映射的方式,只须要赋值一次就能够,因此效率比管道高。
学习



4、什么是AIDL

Android Interface Definition Language,接口定义语言。

Binder是Android系统中进程间通信的一种方式,也是Android系统中最重要的特性之一。 AIDL就是定义自动生成Binder相关代码的语言。

咱们已经知道了进程间通讯最好的方式是使用binder,那么咱们应该如何告知系统咱们想作什么事情呢,最简单使用最多的方式就是AIDL语言了,咱们只须要经过AIDL语言进行编程,接下来AIDL会自动帮咱们生成binder文件,节省咱们的时间。接下来咱们经过一个demo来演示一下客户端、服务端是如何经过AIDL进行交互的,咱们让客户端、服务端分别做为一个独立app可能会更加清晰明了。

步骤一   在服务端app中新建aidl文件

选中包名,右键new->aidl->aidl file,就会弹出给aidl取名的弹框,在里面输入名字肯定,系统就会自动在和java同级目录下新建aidl文件夹,而且新建好了aidl文件。内容很简单,首先导入了咱们自定义的实体类,而后写好了添加和获取列表的2个方法供客户端来调用。


步骤二   服务端新建一个数据实体类Person做为待传输数据

须要注意的是,这里也要先新建AIDL文件,而后再新建Person文件。而且须要特别注意的是,这个AIDL文件的名字必定要和javaBean名字同样,即文件名必须叫Person.aidl。否则是会报错的,第一次发现的时候调很久,血的教训。。。新建好Person类之后须要实现Parcelable接口,表明能够传输数据,此次我在Person类中定义了2个属性name和grade。


步骤三 服务端建立服务service

这里面功能很简单,至关因而一个仓库,保管着全部的实体类对象。最后须要在清单文件中注册service,而且加上android:exported='true'表明当前服务是公开出去,容许其余进程调用。

步骤四  服务端中启动service

在服务端的某个地方启动service,这里咱们在打开服务端app的首页时直接启动。


步骤五  客户端配置AIDL和javabean

aidl:只须要将服务端新建的2个aidl文件所有复制到客户端便可,注意这里必定要保证同样才能进行通讯,因此通常都是直接复制过去就好。

javabean:这里须要注意,必须新建一个和服务端如出一辙的包,而后将服务端的Person复制过来,保证在客户端也能调用到该类。

步骤六  客户端使用服务端提供的接口进行通讯

我在客户端这边写了一个按钮,每次点击之后调用服务端的addPerson方法添加一个用户,而后将用户信息打印出来。这里须要注意的是,须要绑定好服务端的服务,具体代码以下:


步骤七  运行代码查看结果

这里先运行服务端,而后运行客户端,运行后在客户端的logcat成功打印出日志,说明跨进程调用成功。

binder使用总结:这里咱们使用了aidl语言来操做binder实现了跨进程的调用,而实际上aidl的做用仅仅是用来生成binder文件而已,只不过是这个生成的文件比较复杂,可读性低,通常不本身写。若是咱们能本身写出该binder文件的话彻底能够不用aidl了,该文件能够在build中查看到,并且客户端、服务端中都有而且内容如出一辙。固然aidl也有缺陷,就是编写aidl文件的时候没有报错提示,因此调试起来比较麻烦。以上咱们简单使用了binder实现进程间通讯,接下来咱们再一块儿分析一下binder核心原理。



5、binder源码分析

咱们能够从aidl生成的binder代码来入手分析,继承自IInterface.

接下来点击AS左侧的选项查看该类的结构图,在核心的Stub中有2个成员,一个是Proxy,另外一个是Stub(),其中proxy是发送数据,stub是接收数据。


服务端可能会有不少的aidl文件,具体调用哪一个是经过attachInterface进行绑定的。


接下来咱们来看看asInterface方法,该方法在客户端调用,而后在服务端实现。客户端传入了binder,而后返回了一个aidl对象。调用以后首先搜寻本地的aidl接口,排除不跨进程的状况,而后再调用proxy()将客户端的引用传给binder,binder拿到了引用之后就能够操做客户端了。



接下来再看看核心的proxy内部类里有什么,是否是似曾相识,这里面addPerson、getPersonList方法均为服务端定义的供客户端使用的方法。其中_data是客户端传给服务端的数据,而_replay是服务端处理完返回给客户端的结果。最后经过binder对象的transact将当前进程挂起,而后调用服务端中的方法。这里的Stub.TRANSACTION_addPerson是谷歌官方考虑到传递方法名字符串比较耗性能,而后服务端和客户端的方法和顺序是如出一辙的,因此这里就直接用数字来代替,表明第几个方法。这也从侧面说明了客户端和服务端定义的aidl文件为什么要一致了,不一致的话这里是会找不到指定方法的。注意其中的flags若是为0表明双向,若是为1表明单向。


在调用完transact方法后就会调用服务端的onTransact(),咱们找到服务端的onTransact方法一看究竟。这里就很清晰明了了,判断刚刚传过来的方法下标,而后进行指定的处理,处理完之后将结果经过writeXXX存到reply中,因此前面的_reply就有值了。



总结:本文对多进程的使用场景进行了分析,还简单提到了多种跨进程的方式,通过对比AIDL的性能会更高。因此分享了经过AIDL语言生成了binder调用代码实现了跨进程通讯,进程A成功获取到并修改了进程B的值。而且查看了AIDL生成的binder文件的源码,知道了AIDL语言的做用,以及简单分析了binder工做的一整套流程。可是binder其实还有更多复杂的内容须要去学习,好比binder为何要借助service来完成通讯等,之后有空会继续学习和更新。

相关文章
相关标签/搜索