在android中进行跨进程通信通常有以下几种方式:
其中Intent中可以携带Bundle,而Bundle实现了Parcelable接口,所以可以在不同的进程间进行传输。文件共享就是读写文件,比如常用的SharedPreferences就是以XML来存储的文件。而Messenger,AIDL,ContentProvider都是基于Binder的,所以学习并理解Binder就尤为重要。
虽然Android继承使用Linux的内核,但Linux与Android的通信机制不同。
在Linux中使用的IPC通信机制如下:
传统IPC:无名pipe, signal, trace, 有名管道
AT&T Unix 系统V:共享内存,信号灯,消息队列
BSD Unix:Socket
而在Android中,并没有使用这些,取而代之的是Binder机制。Binder机制是采用OpenBinder演化而来,在Android中使用它的原因如下:
采用C/S的通信模式。而在linux通信机制中,目前只有socket支持C/S的通信模式,但socket有其劣势,具体参看第二条。
有更好的传输性能。对比于Linux的通信机制,
socket:是一个通用接口,导致其传输效率低,开销大;
管道和消息队列:因为采用存储转发方式,所以至少需要拷贝2次数据,效率低;
共享内存:虽然在传输时没有拷贝数据,但其控制机制复杂(比如跨进程通信时,需获取对方进程的pid,得多种机制协同操作)。
安全性更高。Linux的IPC机制在本身的实现中,并没有安全措施,得依赖上层协议来进行安全控制。而Binder机制的 UID/PID是由Binder机制本身在内核空间添加身份标识,安全性高;并且Binder可以建立私有通道,这是linux的通信机制所无法实现的 (Linux访问的接入点是开放的)。
那什么是Binder呢?
Binder通信机制流程(整体流程)
首先server会通过Binder向ServiceManager注册自己的服务,Client想要使用远程服务时会通过Binder去ServiceManager申请服务,申请成功之后便可以取得和Server的联系了,可以调用Server端的方法。
这里面一共有四个角色,分别为Client,Server,Binder,ServiceManager
需要注意的一点是,Client通过Binder来跨进程取得Server的联系方式,但Client与ServerMangaer的通信,Server与ServerManager的通讯也是跨进程,这个问题就像买匡威鞋子一样
哈哈哈,这个问题就要从系统刚刚启动说起了,init进程会启动一个叫ServiceManager的进程,该进程启动之后会做三件事:
server向ServiceManager注册服务流程图
Service向ServerManager中注册自己会调用Service.addService(String name,IBinder service),传入的内容是 Server的名字和该Server驱动中对应的对象;
这样,系统服务就注册到ServiceManager中了;
Client向ServiceManager获取服务流程图
Client和Binder驱动通信的时候会调用ServiceManager.getService(String name)方法,传入的参数就是要请求的服务的名字,返回对应请求的Binder。
这样系统服务是怎么注册到ServiceManager里面以及我们怎么获得这些服务对应于Binder驱动的句柄也就是Binder对象的过程讲解就已经结束了,接下来便是我们自定义服务是怎么通过Binder进行进程间通信的呢?
这里就要用到Android为我们自定义进程间通信所提供的AIDL文件了,没有这个文件,你也可以实现跨进程通信,但是序列化,反序列化数据的封装都将需要你自己来实现,有了AIDL之后这个过程会显的比较简单,你并不需要关心序列化反序列化的顺序问题,只需要将Server进程想要提供给Client进程访问的方法定义在一个.aidl文件中即可,我们在此将他命名为Ixxx.aidl,那么系统将会为该AIDL文件自动生成对应的 Ixxx.Java文件;
简单说说Ixxx.java的类结构,将是下面这样的伪代码形式:
public interface Ixxx extends IInterface{
public static abstarct class Stub extends Binder implements Ixxx{
public static Ixxx asInterface(Binder binder){}
public Binder asBinder(){}
public boolean onTransact(int code,Parcel data,Parcel reply,int flags){}
private static class Proxy implements Ixxx{
public Binder asBinder(){}
//Ixxx接口中的一些实现方法,当然这些实现并不是真正的逻辑实现,而只是通过transcat进行的一些进程切换操作
}
}
}
可以看到Ixxx.java中存在一个Stub静态抽象类和Prxoy静态类,最关键的方法就是Stub类中的asInterface了,他会根据我们传入的Binder对象来判断是跨进程通信还是进程内部通信,如果是进程内部通信,该方法返回的将是Ixxx.Stub对象;如果是跨进程通信返回的将是Ixxx.Stub.Proxy对象,这个代理对象中将的方法会通过调用transact方法来进行内核态的切换;
这也便是我们自定义服务实现进程间通信的简单过程了;
在使用AIDL实现Binder通信的过程中,我们应该注意一点的就是,AIDL中不管是服务端方法还是客户端方法都是运行在各自的Binder线程池中的,同时客户端的线程会被挂起,如果服务端方法执行比较耗时,就会导致客户端线程长时间阻塞,导致 ANR 。并且如果我们想要更新UI的话,还需要用到Handler进行切换操作;
之前在介绍Android使用Binder机制的优点中,提到Binder可以建立点对点的私有通道,匿名Binder就是这种方式。在 Binder通信中,并不是所有用来通信的Binder实体都需要注册给SM广而告之的,Server可以通过已建立的实体Binder连接将创建的 Binder实体传给Client。而这个Binder没有向SM注册名字。这样Server与Client的通信就有很高的隐私性和安全性。