AIDL原理
AIDL是依赖Parcel,配合Binder来实现不同进程之间的数据传输。我们拿自定义的AIDL举例子。
自定义了一个AIDL服务,如下:
interface ITargetService {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String getTitle();
void start();
}
对于这个targetService,我们客户端想要调用服务端方法时候,需要通过ServiceConnection回调回来的IBinder对象转成我们对应的服务对象,然后调用目标方法。示例代码如下:
bindService(i, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service){
iTestAIDL = ITargetService.Stub.asInterface(service);
iTestAIDL.start()
}
这里其实会有疑问,我们通过ServiceConnection回调拿到的IBinder对象究竟是什么?因为服务对象是在另外一个进程中,跨进程为何可以拿到其对象呢?所以这里IBInder肯定不是直接就拿到了真正的服务对象,应该是其他一种可以和服务对象绑定的东东。详情我们看下方原理图。
从图中可以知道:
- APP进程会创建一个ServiceConnection服务等到IBinder的传递,然后将其句柄通过parcel机制传递给系统服务进程
- 系统服务进程通过AMS启动,服务所在的APP进程,并且拿到其服务的句柄
- 系统服务进程通过ServiceConnection的句柄调用其方法,将目标服务的句柄再传递给APP 进程。
- APP进程ServiceConnection回调触发,收到IBinder对象,也就是目标服务的句柄。
这个过程中,传递的这些句柄,其实是封装到了BinderProxy对象中的,所以返回的IBInder对象,其实例都是一个包含句柄的BinderProxy对象。
Parcel(mmap)
从这个流程图中,可以看到贯穿整个流程的其实是对象parcel,binder在这个流程中,主要起的作用是作为一个接口,可以让我们在不同进程中不用关心其具体实现,直接在AIDL中可以使用。真正作用进程之间通信的是parcel。
那么parcel中是什么原理实现进程间通信呢?其实是mmap技术。
mmap
mmap刚出来时候是为了解决LInux内核进程和用户进程之间通信,数据拷贝冗余的问题。
对于Linux系统,分为用户空间和内核空间。内核空间是系统服务和接口运行的地方,用户空间则是给用户自定义的应用使用。但是用户自定义的应用免不了需要用到内核空间里的方法,但是因为Linux为了安全考虑,用户空间不能直接操作内核空间里的数据,所以没法直接交互。而是有内核空间暴露接口出来给用户空间使用。
这样对于数据的交换就存在这样一个流程了:
用户数据由用户空间拷贝到内核空间缓存区,内核空间再拷贝数据到页缓存中,页缓存再对数据操作,放到持久化内存中。 中间内核空间缓冲区中的数据就有点多余了,是否可以直击跳过这一步,直接将数据放到页缓存中呢?mmap技术就随之而出来了。
mmap其实就是一种映射关系,只不过在这里相当于是内核空间页缓存的映射。在用户空间会生成一个虚拟内存,直接映射关联到内核空间的页缓存,后续用户空间对这个虚拟内存操作读写时候,就相当于直接操作了内核空间的页缓存。省了一次数据拷贝。
这个就是真正的mmap,然后现在呢,mmap已经成为了一种技术的代名词,mmap主要就是对一个真正的内存做了映射关系,所以可以在其他进程对这个映射做操作,等于可以直接操作到另外一个进程中的真正的内存。所以在Android的AIDL中,parcel中主要就是用了mmap原理,在主服务进程中存在真正的内存,子进程(客户端)中开辟内存映射,从而达到子进程(客户端)调用服务端的方法。
原理图如下:
至此,Android系统的核心,也就是AIDL,由上到下,都已经梳理完了。了解了这个,对于看Android源码有很大帮助。