进程间通信方式
- 1、为什么android需要采用binder
IPC
- 两次拷贝,先调用cope_form_user拷贝到缓存区,然后再拷贝到缓存区
Bind
- 服务端和客户端都会映射到同一个空间
- 通过mmap的内存映射,内核缓存区和服务端是两个都处于同一个空间
- 共享内存是客户端,服务端和内核缓存区是处于一个空间,所以无需拷贝,问题是需要多次同步,所以存在问题
- 很安全,APP是由系统分配UID的,并且支持实名
AIDL
自动生成的AIDL文件解析
客户端
public interface ILeoAidl extends android.os.IInterface {
/** 继承了binder 和aidl类 抽象类*/
public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl{
private static final java.lang.String DESCRIPTOR = "com.xx.leo_service.ILeoAidl";
public Stub(){
//
this.attachInterface(this, DESCRIPTOR);
}
public static com.xx.leo_service.ILeoAidl asInterface(android.os.IBinder obj){
//判断是否为空
if ((obj==null)) {
return null;
}
//查询本地的接口是否相等
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//如果在同一个进程,就直接返回,否则就代理
if (((iin!=null)&&(iin instanceof com.xx.leo_service.ILeoAidl))) {
//直接返回
return ((com.xx.leo_service.ILeoAidl)iin);
}
//返回代理
return new com.xx.leo_service.ILeoAidl.Stub.Proxy(obj);
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case TRANSACTION_addPerson: {
data.enforceInterface(DESCRIPTOR);
com.xx.leo_service.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.xx.leo_service.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPersonList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.xx.leo_service.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//实体类 直接实现AIDL
private static class Proxy implements com.xx.leo_service.ILeoAidl{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
//直接实现自定义方法
@Override
public void addPerson(com.xx.leo_service.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
//校验,每个服务都有唯一的DESCRIPTOR
_data.writeInterfaceToken(DESCRIPTOR);
if ((person!=null)) {
//传入数据
_data.writeInt(1);
person.writeToParcel(_data, 0);
}else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<com.xx.leo_service.Person> getPersonList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.xx.leo_service.Person> _result;
try {
//校验,每个服务都有唯一的DESCRIPTOR
_data.writeInterfaceToken(DESCRIPTOR);
//调用服务端的代码
mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
//读取看是否有
_reply.readException();
_result = _reply.createTypedArrayList(com.xx.leo_service.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//传入一个整型,按顺序拿到方法
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void addPerson(com.xx.leo_service.Person person) throws android.os.RemoteException;
public java.util.List<com.xx.leo_service.Person> getPersonList() throws android.os.RemoteException;
}
服务端
public interface ILeoAidl extends android.os.IInterface {
public void addPerson(com.xx.leo_service.Person person)
throws android.os.RemoteException;
public java.util.List<com.xx.leo_service.Person> getPersonList()
throws android.os.RemoteException;
/** L核心方法 stub*/
public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl {
private static final java.lang.String DESCRIPTOR = "com.xx.leo_service.ILeoAidl";
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION +
0);
static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION +
1);
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.xx.leo_service.ILeoAidl asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.xx.leo_service.ILeoAidl))) {
return ((com.xx.leo_service.ILeoAidl) iin);
}
return new com.xx.leo_service.ILeoAidl.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
//对应客户端的onTransact方法
@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
//根据整型来拿到方法
switch (code) {
case TRANSACTION_addPerson: {
data.enforceInterface(DESCRIPTOR);
com.xx.leo_service.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.xx.leo_service.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPersonList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.xx.leo_service.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
}
ServiceManager
我们自动生成的aidl和系统的服务是一一对应的。
ServiceManage负责管理所有的ServiceManager服务,在APP启动的时候,会调用电量,媒体等多个服务,都是通过binder来进行调用的,所有的服务端和客户端通信都是通过ServiceManager来注册和获取服务的。
A进程访问B进程时的几种状态
- 进程B,整个进程都没有启动
- 进程B启动了,但是里面的Service没创建出来
- 进程B启动了,里面的Service也创建了,但是Service没有被绑定过,回调onBind()
- 进程B启动了,里面的Service也创建了,但是Service已经被绑定过,回调onRebind()
5个角度来分析Binder
- 从性能的角度 数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存
- 从稳定性的角度
- Binder是基于C/S架构的 C/S 相对独立,稳定性较好
- 共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题
- 从安全的角度
- 传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份
- 传统IPC只能由用户在数据包里填入UID/PID
- 可靠的身份标记只有由IPC机制本身在内核中添加
- 传统IPC访问接入点是开放的,无法建立私有通道
- Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行
- Android的UID权鉴是如何做的?
- 传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份
- 从语言层面的角度
- Linux是基于C语言(面向过程的语言),而Android是基于Java语言(面向对象的语句)
- Binder恰恰也符合面向对象的思想 Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中
- Android OS中的Zygote进程的IPC采用的是Socket(套接字)机制,Android中的Kill Process采用的signal(信号)机制等等。而Binder更多则用在system_server进程与上层App层的IPC交互。
- 从公司战略的角度
总所周知,Linux内核是开源的系统,所开放源代码许可协议GPL保护,该协议具有“病毒式感染”的能力,怎么理解这句话呢?受GPL保护的Linux Kernel是运行在内核空间,对于上层的任何类库、服务、应用等运行在用户空间,一旦进行SysCall(系统调用),调用到底层Kernel,那么也必须遵循GPL协议。 而Android 之父 Andy Rubin对于GPL显然是不能接受的,为此,Google巧妙地将GPL协议控制在内核空间,将用户空间的协议采用Apache-2.0协议(允许基于Android的开发商不向社区反馈源码),同时在GPL协议与Apache-2.0之间的Lib库中采用BSD证授权方法,有效隔断了GPL的传染性,仍有较大争议,但至少目前缓解Android,让GPL止步于内核空间,这是Google在GPL Linux下 开源与商业化共存的一个成功典范。
mmap:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
- 1、在物理内存开辟物理区域(用于存储数据)
- 2、将这个物理区域与磁盘进行映射
- 3、将物理地址转化为虚拟地址,返回给应用层