一、Linux内核的基础知识
1、进程隔离和虚拟地址空间
在操作系统中,为了避免进程于进程之间互不干扰,于是就设计了一种叫进程隔离的技术,而这种技术就是避免进程A去操作进程B中的数据所设计的,而这种技术要用到虚拟地址,进程A和进程B的虚拟地址不一样,所以两个进程之间数据是不共享的。
如果想要进程间进行通信,则需要一种进程间的通信机制才可以完成,在Android中就是Binder通信机制
2、系统调用
在Linux内核中有有一个很重要的概念就做系统调用,可以让应用程序只能访问某些许可的资源,不许可的资源是不允许访问的
3、binder驱动
负责不同进程之间进行交互的模块,驱动一般指设备得驱动程序,也可以是计算机程序和设备通信得一种程序
二、Binder通信机制的介绍
1、为什么要用Binder
Android使用的Linux内核有着很多跨进程间的通信机制,为什么还要用Binder作为Android特有的进程间通信机制呢?
1、性能:在移动设备上广泛的使用跨进程间通信肯定要对通信机制有更高的要求,Binder机制比其它的通信机制要更加高效
2、安全
2、Binder通信模型
进行跨进程间通信的双方,可以成为客户端进程Client和服务端进程Server
由于进程隔离的存在,我们不能通过客户端进程访问到服务端进程的。
举个例子:
A同学要给B同学打电话,首先得先找到B同学的电话号码,去哪里找呢,当然是通讯录,因为通讯录里面记录着每个人的名字和与之对应的电话号码,这样A同学通过通讯录查找B同学的电话号码,这样就能给B同学打电话了,当然仅仅有电话号码还不够,必须还要电话基站的支持,因为电话基站可以传递双方电话信号的。
通过上面的例子,我们知道A和B打电话,除了A和B,还有两个非常重要的概念,那就是通讯录和电话基站
用这个例子映射到Binder通信机制,两个运行在用户空间的进程想要进行通信,不仅仅只需要客户端和移动端,必须借助内核的帮助,而这个运行在内核中的程序就是Binder驱动,它的功能类似我们的电话基站,而通讯录就是一个叫ServiceManager的东西
通讯录:ServiceManager
电话基站:Binder驱动
通信的步骤:
结合上面的例子:
1、首先得有一个独立的进程向Binder驱动申请为ServiceManager,经过驱动同意后,ServiceManager就开始管理所有的电话号码,这时候还没有同学向ServiceManager通讯录中注册,这时候ServiceManager里还没有电话号码。
2、这时候A同学想要联系B同学,必须要把联系方式在ServiceManager中注册,在每个服务端启动之后都会在ServiceManager中注册,将联系方式和地址保存在ServiceManager中,ServiceManager里建立一张表,里面记录着每个同学的电话名字和电话号码。
3、Client如果想要和Server2进行通信,则必须先查询ServiceManager,获取到Server2的联系方式,这样Client拿到Server2的联系方式,通过Binder就可以实现与Server2的通信。
如何进行跨进程间通信:
1、首先Server向ServiceManager中注册,告诉ServiceManager自己有一个Object对象,并且有一个add()方法,于是ServiceManager就建立好一张表,这时Client查询ServiceManager,又没有一个Object对象,该对象有没有一个add()方法,(进程之间的通信,数据都是在内核空间里),驱动在数据的传输过程中会做一些手脚,并不会把Server端的Object对象直接返回给Client,而是返回给Client一个代理的对象,而这个代理对象包含了一个add方法,但是这个add方法是个空方法,里面是什么都没有,什么都不做,Client调用这个addf方法,实际上是让驱动中的Server端的Objectd对象替换这个Client的代理Object对象,实际上是通知Server端调用add方法,然后把调用结果返回给驱动,然后驱动又把结果返回给Client.
总结:Client端只是拿到服务端的代理对象,Client拿到这个代理对象再配合驱动去实现和Server端的跨进程通信
3、Binder通信的原理
到底什么是Binder?
1、Binder其实就是一种跨进程的通信机制
2、对于Server端而言Binder指的是Binder本地对象,对于服务端而言,Binder指的是Binder代理对象
3、对于传输而言,Binder是可以进行跨进程传递的对象
三、Binder的实例AIDL
1、AIDL的基本理论
aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口
2、AIDL远程调用案例
1、定义aidl文件
定义:
2、定义服务端
同步代码,定义Service,并在清单文件注册
必须写上Action
3、定义客户端
将服务端aidl文件夹整个拷贝过来
AIDL默认传递的数据类型:
基本数据类型(Short类型不支持),String、CharSequence、List(集合里面的元素必须是支持的类型)、Map、Parcelable
代码实现:
Serializable使用IO读写存储在硬盘上,而Parcelable是直接在内存中读写,很明显内存的读写速度通常大于IO读写,所以在Android中通常优先选择Parcelable
public class Person implements Parcelable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(Parcel source) {
//取数据的顺序要和存数据的数据一致
this.name=source.readString();
this.age=source.readInt();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public static final Creator<Person> CREATOR=new Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class IRemoteService extends Service {
private ArrayList<Person> persons;
/**
* 当客户端绑定到该服务的时候调用
* @param intent
* @return
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
persons = new ArrayList<>();
return mIBinder;
}
private IBinder mIBinder = new IMyAidlInterface.Stub() {
@Override
public List<Person> add(Person person) throws RemoteException {
persons.add(person);
return persons;
}
};
}
客户端Person和aidl包名必须和服务端一致
demo已上传至GitHub
3、AIDL原理简单剖析