AIDL和Binder简介
- 他们都与IPC(远程调用)有关
- Binder是一个实现IBinder的类,提供了两个与Binder驱动通信的重要接口方法,你可以通过它实现自定义的RPC协议(
Client
、Server
、Service Manager
)
(1)Transact()
:客户端调用,用于发送调用请求
(2)onTransact()
:服务端响应,用于接收调用请求 - Service与客户端通信,有两种方式,AIDL和Messenger。AIDL基于Binder,而Messenger基于AIDL。
- AIDL是android提供的接口定义语言,借助这个工具,你可以很轻松地实现IPC通信机制,根据需要灵活定义接口。
- 作用范围:
(1)Binder:如果是在一个应用里实现远程调用,使用Binder即可,没必要使用AIDL。
(2)AIDL:如果涉及到在多个应用程序之间使用IPC通信,并且在服务又有多线程业务处理,这时可以使用AIDL。
Binder
注意:Client、Server、ServiceManager之间是相互独立互不干涉,都是通过Binder驱动进行交互的
Binder的进程间通信,一共有四个角色:
Client:客户端(使用服务的进程)
Server:服务端(提供服务的进程)
ServiceManager:
(1) ServiceManager 是 Binder 进程间通信的核心组件之一,扮演者 Binder 进程间通信机制的上下文管理者 ( Context Manager ) 的角色。
(2)负责管理系统中的 Service (如AMS
)组件,并且向 Client 组件提供获取 Service 代理对象的服务(开启循环,处理IPC请求)。-
Binder驱动:底层的驱动架构与Linux驱动一样。binder驱动在以misc设备进行注册,作为虚拟设备,没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的初始化
binder_init
,打开binder_open
,映射binder_mmap
,数据操作binder_ioctl
。
(1)binder_init
: 注册驱动
(2)binder_open
: 创建一个struct binder_proc数据结构来保存打开设备文件/dev/binder的进程的上下文信息
(3)binder_mmap
: 进程空间和内核空间的虚拟地址映射到同一个物理页面,从而达到进程空间和内核空间共享一块物理页面的目的为什么要这么映射地址呢?
把同一块物理页面同时映射到进程空间和内核空间时,当需要在两者之间传递数据时,只需要其中任意一方把数据拷贝到物理页面,另一方直接读取即可,也就是说,数据的跨进程传递,只需要一次拷贝就可以完成。
传统的跨进程通信(发送进程发送数据到接收进程):
第一次拷贝过程:发送进程—>用户空间—>(拷贝一次数据)—>内核空间
第二次拷贝过程:接收进程—>内核空间—>(拷贝一次数据)—>用户空间Binder(内存映射)跨进程通信:
第一次拷贝过程:发送进程—>用户空间—>(拷贝一次数据)—>内核空间(
用户空间和内核空间映射到同一段物理地址,这样第一次复制到内核空间,其实目标的用户空间上也有这段数据了
)—>用户空间这样相当于少了一次复制
Binder和其他进程间通信的区别,总结一下,Binder具有以下优点:
- 高效:Binder拷贝数据只需要1次,管道、Socket、消息队列都需要2次
- 使用简单:采用C/S架构,实现面向对象调用方式,使用Binder跟调用本地对象一样操作简单
- 安全性高:Binder给每个进程分配UID/PID作为身份标识,通信时会根据UID/PID校验身份,其他通信方式没有严格的校验过程
(4) binder_ioctl
: 两个进程间收发IPC数据和IPC reply数据
除了Binder进程通信还有哪几种?
共享内存(Share Memory)(需拷贝0次)
共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间中。其他进程可以将同一段共享内存连接到自己的地址空间中。所有进程都可以访问共享内存中的地址,如果一个进程向共享内存中写入了数据,所做的改动将立刻被其他进程看到。
共享内存是IPC最快捷的方式,共享内存方式直接将某段内存段进行映射,多个进程间的共享内存是同一块的物理空间,仅仅映射到各进程的地址不同而已,因此不需要进行复制,可以直接使用此段空间
。
共享内存本身并没有同步机制,需要程序员自己控制。
内存映射(Memory Map)
内存映射是由一个文件到一块内存的映射,在此之后进程操作文件,就像操作进程空间里的内存地址一样了。
管道 (需拷贝2次)
管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。
特点:
- 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
- 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
- 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
消息队列 (需拷贝2次)
是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点:
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
- 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
信号量
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
特点:
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。P操作是(pass通过)获取操作权,如果当前信号量大于0则使信号量减1并获取资源,否则阻塞等待;V操作是释放操作权,使当前信号量加1,若信号量大于0则从等待的P操作中唤醒一个继续执行。
- 每次对信号量的 PV 操作不仅限于对信号量值减 1 或加 1,而且可以加减任意正整数。
- 支持信号量组。
Socket (需拷贝2次)
Binder实例
Client-ServiceManager-Server时序图
流程如下:
- 第一步Server端通过Binder驱动向ServiceManager注册信息,在Binder驱动创建
mRemote
对象。(传递的参数打包成Parcel对象transact()
) - 第二步Client端通过Binder驱动向ServiceManager请求Server的Binder引用,然后Binder驱动将对应的
mRemote
对象返回 - 第三步Client获得Binder引用(
mRemote
对象,该对象对应如下示例中的MyServiceProxy
对象)后,通过调用mRemote
对象中的callHi
方法将传递的参数打包成Parcel对象再通过transact()
向Binder驱动层写入,这个时候Client端的线程会挂起,而Server端中的onTransact
方法收到Binder驱动层的回调后,会进行执行指令从而调用真正的callHi
执行其逻辑,执行成功后会调用reply.writeNoException
进行应答,而Binder驱动收到应答后会唤醒Client端被挂起的线程
。
如果还是看不懂,我们直接上栗子来看吧,这样会更加直观些。
1 Server端
1.1 定义IMyService接口
public interface IMyService extends IInterface {
//DESCRIPTOR是唯一标识
static final java.lang.String DESCRIPTOR = " com.shengyuan.Server";
//TRANSACTION_say是binder通信的cmd
static final int TRANSACTION_say = android.os.IBinder.FIRST_CALL_TRANSACTION;
//service中的接口,提供给客户端用实现某些功能
public void callHi(String str) throws RemoteException ;
}
1.2 继承Binder,实现IMyService接口,重写onTransact方法
public class MyService extends Binder implements IMyService{
//继承自Binder实现IMyService接口
public MyService() {
this.attachInterface(this, DESCRIPTOR);
}
@Override
public IBinder asBinder() {
return this;
}
public static com.shengyuan.IMyService asInterface(android.os.IBinder obj) {
if ((obj == null)) return null;
android.os.IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR);
if (( iInterface != null && iInterface instanceof com.shengyuan.IMyService)){
return ((com.shengyuan.IMyService) iInterface);
}
return null;
}
@Override
protected boolean onTransact(
int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//该方法主要用于接收binder驱动的回传信息
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_say: {//响应命令
data.enforceInterface(DESCRIPTOR);
String str = data.readString();
sayHello(str);
reply.writeNoException();
return true;
}}
return super.onTransact(code, data, reply, flags);
}
@Override
public void callHi(String str) { //实现接口
System.out.println("MyService:: Hello, " + str);
}
}
1.3 作为Server端需调用ServiceManager.addService方法进行注册
public class ServerDemo {
public static void main(String[] args) {
Looper.prepareMainLooper();
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
ServiceManager.addService("MyService", new MyService()); //注册service
Looper.loop();
}
}
2 Client端
2.1 实现IMyService接口的代理
public class MyServiceProxy implements IMyService {
private android.os.IBinder mRemote;
public MyServiceProxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void callHi(String str) throws RemoteException {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeString(str);
mRemote.transact(TRANSACTION_say, _data, _reply, 0);//通过获取的mRemote(即service在本地的代理)发送命令
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
}
2.2 与Server端通信
public class ClientDemo {
public static void main(String[] args) throws RemoteException {
//调用getService方法,会根据传入的名称参数打包成Parcel对象调用
//transact方法写入给binder驱动,Binder驱动将对应的mRemote对象返回
IBinder binder = ServiceManager.getService("MyService");
IMyService myService = new MyServiceProxy(binder);
myService.callHi("binder");
}
}
AIDL
AIDL即Android Interface Definition Language(安卓接口定义语言),当我们创建了这个接口后,系统会自动生成其对应的Binder类,它继承了IInterface, 内部有一个静态抽象类Stub和Stub内部的Proxy类。其中Stub继承了Binder类,所以AIDL中的Stub即为一个Binder对象。 在服务端实现该接口后,支持在客户端远程调用(RPC)。
综上AIDL定义的接口,它除了是一个接口以外,它还是一个Binder对象,支持在接口和Binder之间相互转换(asBinder(), asInterface())。
一个进程既可以是服务端Stub,也可以是客户端Proxy。
一个IPC请求发起时,首先会调用Proxy去连接Binder驱动,然后Binder驱动再去连接Stub
要实现跨进程通信,两个进程必须要有相同的AIDL接口
bindservice绑定流程时序图
注意:ContextImpl.bindServiceCommon中调用ActivityManagerNative.getDefault().bindService这个方法,是涉及到(Client-ServiceManager-Server)通信,具体可以看其Binder实例中的时序图。
流程如下:
- 第一步定义AIDL接口
- 第二步实现AIDL文件生成的JAVA接口
Stub
(即stubSerVice
类) - 第三步定义一个自己的Service,在实现自己的Service时,为了让Client端可以通过
bindService
来和我们的Service进行交互,我们都要实现Service中的onBind()
方法,并且返回一个继承了Binder的内部类(即stubSerVice
类) - 第四步Client端调用
bindService
方法进行服务绑定(流程请看如上时序图),服务绑定成功后,serviceConnection.onServiceConnected
接口回调,获取到BinderProxy
对象(即Server端Binder引用
),再利用asInterface
方法将BinderProxy
对象转化为接口对象,从而实现与Server端跨进程通信
AIDL实例
1 定义AIDL接口
接口定义注意
Service升级时,会在aidl文件里增加或修改接口,如果客户端不更新所使用的aidl文件,这就会出现上述不一致的情况。
因为TRANSACTION code
是根据aidl里接口声明的顺序生成的。所以当aidl里面函数的声明顺序改变,或者新加,删除函数,都会造成TRANSACTION code
的值会不同。这样使用旧aidl文件的应用就可能出现问题!
解决办法:
当service升级时,为了避免出现上面的问题,应该保证aidl的变化不影响到旧有接口的TRANSACTION code
。所以新的aidl的编写有以下几个注意点。
新加函数接口应该在旧有函数的后面。
尽量避免删除旧有函数,如果真的要删的话,可以保留函数名字作为占位,返回一个错误码之类的来解决。
不能改变原来的接口声明顺序。
package multichoose.shengyuan.com.mytestdemo;
interface Publicmake {
void dealwith();
}
2 Server端
public class AidlService extends Service {
private static final String TAG = AidlService.class.getSimpleName();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new SstubSerVice();
}
class SstubSerVice extends Publicmake.Stub {
@Override
public void dealwith() {
AidlService.this.callHi();
}
}
public void callHi() {
Log.i(TAG, "MyService:: Hello");
}
}
4 注册Service
<service android:name=".aidldemo.AidlService"
android:process=":remote">
<intent-filter>
<action android:name="AidlService"/>
</intent-filter>
</service>
5 新建AidlActivity绑定Service(Client端)
bindservice成功后 会创建一个IBinder实例 这个时候就可以通过它与另一端通信
public class AidlActivity extends AppCompatActivity {
public Publicmake psb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bt = findViewById(R.id.button);
Intent it = new Intent(this,AidlService.class);
bindService(it, new serviceConnection(), BIND_AUTO_CREATE);
bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clickup(v);
}
});
}
public void clickup(View v) {
try {
psb.dealwith();
} catch (RemoteException e) {
e.printStackTrace();
}
}
class serviceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//注意一定要利用asInterface转为接口对象
psb = Publicmake.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
public interface Publicmake extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements multichoose.shengyuan.com.mytestdemo.Publicmake {
private static final java.lang.String DESCRIPTOR = "multichoose.shengyuan.com.mytestdemo.Publicmake";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static multichoose.shengyuan.com.mytestdemo.Publicmake asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof multichoose.shengyuan.com.mytestdemo.Publicmake))) {
//同一进程内直接返回
return ((multichoose.shengyuan.com.mytestdemo.Publicmake) iin);
}
//不在同一进程使用代理获取远程服务
return new multichoose.shengyuan.com.mytestdemo.Publicmake.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_dealwith: {
data.enforceInterface(DESCRIPTOR);
this.dealwith();
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* 代理类,调用transact方法。
*/
private static class Proxy implements multichoose.shengyuan.com.mytestdemo.Publicmake {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void dealwith() throws android.os.RemoteException {
// 输入参数
android.os.Parcel _data = android.os.Parcel.obtain();
// 输出参数
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_dealwith, _data, _reply, 0);
//调用mRemote.transact方法后,会挂起当前线程,等待远程方法执行完后才会继续当前线程
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_dealwith = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public void dealwith() throws android.os.RemoteException;
}
Binder和AIDL实例异同
综上,你会发现其实Binder实例和AIDL实例有很多相似之处。
Binder实例中Server端会先通过Binder驱动向ServiceManager
进行注册,Client端则通过Binder驱动与AMS通信获得远程引用对象(proxy),然后通过这个远程引用对象与Server端通信。
而AIDL其实是一个匿名的Binder传输过程,并没有在ServiceManager
中进行注册,匿名binder必须是建立在一个实名binder之上的,也就是指这个实名binder必须在ServiceManager
中注册过的,而AIDL实例中通过调用bindService从而与AMS通信(AMS服务在ServiceManager
中注册),调用ActivityManagerNative.publishService
方法将传入的IBinder对象
打包成parcel
调用transact
方法传入binder驱动层
,再通过ActivityManagerNative.onTransact
方法获取binder驱动返回的Server端binder引用
(即BinderProxy
),再调用其子类即ActivityManagerService.publishService
(ActivityManagerService继承于ActivityManagerNative)方法,回传给客户端,从而让Client端获取到远程binder对象引用从而与Server端远程通信。
主要源码方法解析
ActivityManagerNative.java
时序图1.2.9
在handleBindService 方法中 调用ActivityManagerNative.getService().publishService进入
public void publishService(IBinder token,
Intent intent, IBinder service) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
intent.writeToParcel(data, 0);
data.writeStrongBinder(service);
mRemote.transact(PUBLISH_SERVICE_TRANSACTION, data, reply, 0);
//调用mRemote.transact方法后,会挂起当前线程,等待远程方法执行完后才会继续当前线程
//transact方法 客户端调用,用于发送调用请求
reply.readException();
data.recycle();
reply.recycle();
}
//onTransact方法 服务端响应,用于接收调用请求
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
···
case PUBLISH_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
Intent intent = Intent.CREATOR.createFromParcel(data);
IBinder service = data.readStrongBinder();
//此处的IBinder对象是Binder驱动层转换成BinderProxy返回回来的
//因为ActivityManagerService继承于ActivityManagerNative,且ActivityManagerService重写了publishService方法,
//所以如下调用的是ActivityManagerService.publishService
publishService(token, intent, service);
reply.writeNoException();
return true;
···
}
}
ActivityManagerService.java
public void publishService(IBinder token, Intent intent, IBinder service) {
synchronized(this) {
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
ActiveServices.java
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
if (r != null) {
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
try {
//binder在onTransact回调中已转化成BinderProxy,通过c.conn.connected回调最终进入ServiceConnection的onServiceConnected接口方法中传回给Client端,
//从而让Client端获取到Server端的Binder引用,从而实现远程通信。
//在一开始ContextImpl.java的bindServiceCommon函数中做了一些准备工作,就是创建这个回调类。
c.conn.connected(r.name, service, false);
} catch (Exception e) {
}
}
}
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
onServiceConnected接口方法
bindService是异步调用和Service进行绑定, 如果绑定成功, 则会调用ServiceConnection的onServiceConnected
通过断点,你会发现
onServiceConnected
方法回调回来的service参数是已经转化成BinderProxy
(即Server端Binder对象引用)了,而非通过onBind获取到的IBinder对象,这是为什么呢,通过上面时序图,其实这个对象转换是发生在binder驱动层,可以看时序图1.2.10
那一步,开始将未转化的IBinder对象打包成parcel形式通过transact()
方法传给binder驱动
(时序图1.2.11
),然后通过onTransact
获取Binder驱动返回的BinderProxy
(即Server端Binder对象引用),再通过时序图1.2.15
回传给客户端。
总结
看到这里,其实你会发现,Binder没有想象中难理解,无论是Client-ServiceManager-Server
还是AIDL
进行跨进程通信都离不开Binder,Binder是一个实现IBinder的类,提供了两个与Binder驱动通信的重要接口方法(Transact()
、onTransact()
),你可以通过它实现自定义的RPC协议。
通过阅读引申一些思考,为什么要使用binder来实现进程间的通信呢?
推荐阅读下:https://blog.csdn.net/q1183345443/article/details/69831074