纵观网上大多数文章,会发现大家讨论比较起binder和socket,整片文章不外乎就是基于两个点:
- binder数据一次拷贝,socket两次拷贝
- binder性能比socket好
我们实际在Android使用中都是用的AIDL框架,AIDL底层核心就是binder的封装,如果去对比binder和socket,因为binder少了一次拷贝,所以效率高,但是在实际场景中,对比AIDL和socket时候,性能来考虑,就很难分出胜负了。
socket
先来看下socket原理。
对于socket通信来讲,分为两种socket通信机制,一种是本地socket通信,一种是网络socket通信。
网络socket通信就是我们常见的tcp和UDP协议所使用的socket进行网络数据传输。本地socket主要用于客户端跨进程通信。
不过两种socket通信的底层原理都是基于文件描述符,通过 read() 和 write() 方法就可以实现socket的发送和接收动作。所以对于本地socket来讲,对于客户端和服务端,客户端讲数据先写到用户空间中,然后系统将数据拷贝到内核空间。服务端读取的时候,系统会将数据从内核空间拷贝到用户空间,然后再吐给服务端。
可能大家会有疑问,为什么这里数据要经过一次内核空间这个步骤。因为Linux系统的要求对于敏感操作是在内核空间进行的,所以我们用户空间想要做这些操作,都要调用内核空间的API,内核空间的数据我们没法直接访问。所以对于socket通信,这里需要存在两次拷贝。
binder
对于binder,binder的底层原理是基于mmap。只有一次拷贝。
关于为什么mmap只有一次拷贝,我们继续分析。
首先我们先看下Linux中如何写内容到文件中。Linxu里读写文件仍然需要调用内核空间的方法进行读写,如图:
可以看到,文件读写原理类似本地socket通信原理,毕竟Linux中 "一切皆文件" 。对于正常的将一个文件写入到磁盘中分以下几步。
- 用户空间,也就是应用程序里,获取到准备要写入的内容到用户空间的数据缓存区中,也就是数据存放的位置,比如声明的一个数组。
- 应用程序调用系统API write写入内容,此时会在内核空间开辟一个数据缓存区,会将用户空间数据缓冲区的内容复制到内核空间里。此时,对于应用程序来说,写入成功。
- 内核空间会在系统认为合适的时机,将内核空间数据缓冲区的内容写入到磁盘中。也就是持久化保存。
对于对文件内容的读取,原理也差不多,只是发现内核空间的数据缓冲区没有内容时候,会从磁盘中读取内容。
所以对于文件的读写,会发现有两次数据拷贝。分别是从用户空间到内核空间,然后又从内核空间到用户空间。
Linux这样做保证了内核空间的安全性,但是由于数据的冗余拷贝导致了效率的降低,那么有没有优化方法呢?
我们可以思考,内核空间的安全性是因为用户空间无法直接修改内核空间的内容,所以安全,但是用户空间想要读取里边的数据,其实就不会影响到内核空间的安全性。
所以优化思路就是,写的时候仍然将数据复制到内核空间,读的时候我直接读,不就可以了?
mmap机制就是使用了虚拟地址,在内核空间里直接映射页缓存(数据缓存区)。用户空间读写时候直接对这个虚拟地址进行操作。由于页缓存在内核空间里,所以写数据时候,虽然操作的是虚拟地址,但是实际上仍然需要将应用里(用户空间的数据)的数据拷贝到内核空间中。但是应用读的时候,就可以直接从虚拟地址直接读取内核空间里的数据了,而不用先拷贝出来。所以mmap只需要拷贝一次,即用户空间到内核空间。
binder底层基于mmap实现,所以数据是一次拷贝。
AIDL
AIDL是Android中对binder机制的封装的框架,我们可以使用AIDL简单的快速实现进程之间通信。
对比AIDL和socket
- AIDL 不如socket的场景
对于AIDL,我们在使用过程中,对于两个进程之间,往往都是调用方法,进程A给进程B传递数据后,想要获取这个方法的返回值,但是处理又耗时,方法是异步的,结果通过回调返回,由于数据逻辑在进程B进行的处理,所以其实这里就需要用到两个binder,一个是B作为服务端,A主动调用B的方法,第二个就是A作为服务端,B主动调用A的方法告知结果。
这样来看,其实AIDL通信就又是两次数据拷贝了,而socket这个时候反而比AIDL要简单多了,毕竟socket建立一次即可,互相发送数据通信,而不需要这样建立两个binder去进行通信。另外AIDL中间存在ANdroid的各种SE权限控制,对于时间消耗,对于传输数据的限制也限制了其不如socket。
- socket不如AIDL的场景
有时候,我们仅仅只是简单的想要获取进程B的某些数据,此时如果使用socket,还需要关注建立连接,进行通信,解析数据,而如果使用AIDL机制,直接通过对象引用的方式,面向对象的方式直接通过方法获取到值,且效率比socket要高。
- AIDL适用场景
- Android系统提供的框架,可以控制权限,只让目标程序访问对应进程,适用于ANdroid复杂通信。
- 对于Android开发很友好,比较好理解。
- 适用传递小数据
- socket 适用场景
- 操作简单,适用于传递简单数据
- 适用于传递大数据
Android系统架构大大小小都用的AIDL,AIDL必然尤其过人之处,建议非必要优先选择使用AIDL吧。