请尊重原创,转载请注明出处【tianyl】的博客
前言
说到Android,有一个绕不过去的知识点就是IPC,也叫进程间通信,鉴于市面上已经很多Binder的深入解析,这里我也不说重复的话了,感觉从AIDl的角度聊聊BInder(因为我看好像很少有博客从这个角度切入)
本文概要
1 什么是Binder
2 什么Android要使用Binder作为IPC的方式?Linux现有的IPC方式不能用吗
3 什么是AIDL,AIDL和BInder又是什么关系
4 AIDL的基础用法
5 什么是Stub和Proxy,它们又是如何通信的?
1 什么是Binder
Binder是Android系统进程间通信(IPC)方式之一,说到IPC,就要介绍一些Linux中的IPC方式
2 Linux的IPC方式
- 管道(Pipe)
- 一个线性的内存区域,存消息时将数据写入管道,取消息时从管道拷贝数据,因为有两次拷贝(效率低)
- 插口(Socket)
- 是一个通用接口,导致其传输效率低,开销大,有两次拷贝(效率低)
- 报文队列(Message)
- 有两次拷贝(效率低)
- 共享内存(Share Memory)
- 机制复杂,管理内存机制复杂
- 信号量(Semaphore)
- 信号(Signal)
- 跟踪(Trace)
3 Binder
3.1 Binder的优点
- 安全性
- Linux的IPC机制在本身的实现中,并没有安全措施,得依赖上层协议来进行安全控制。而Binder机制的UID/PID是由Binder机制本身在内核空间添加身份标识,安全性高
- 私有管道
- Binder可以建立私有通道,这是linux的通信机制所无法实现的(Linux访问的接入点是开放的)
- 效率性
- LInux原有的IPC方式,要么是需要多次拷贝(2次或2次以上),要么则是机制复杂,而BInder只需要一次拷贝,在多线程时管理内存也相对容易
所以从效率上和安全性上考虑,Google摒弃了LInux的IPC方式,重新构建了一套属于Android的IPC方式——BInder
4 Binder和AIDL
AIDL全称:Android Interface Definition Language,Binder是IPC的机制,AIDL是Binder的具体规范
5 AIDL详解
关于AIDL,它是Android Interface Definition Language,对于一个.aild文件,它经过IDE的处理后,会生成一个java文件
这个java类会实现android.os.IInterface
接口,并且含有一个Stub的静态抽象内部类
public interface IAIDLTest extends android.os.IInterface
5.1 Stub
仔细查看抽象类Stub,它继承了android.os.Binder
,并实现了我们定义的AIDL接口
public static abstract class Stub extends android.os.Binder implements com.demo.tianyl.demo.IAIDLTest
5.2 Proxy
Proxy是抽象类Stub中的一个静态内部类,它也实现了我们定义的AIDL接口
private static class Proxy implements com.demo.tianyl.demo.IAIDLTest
5.3 Stub和Proxy
介绍完了Stub和Proxy的类的定义,下面来说说AIDL使用时它们之间的关系
1
首先要从我们定义的AIDL说起了,如果我们要使用AIDL,那么我们首先需要定义一个AIDL接口文件(IAidlTest.aidl),根据这个.aidl
文件自动生成一个.java
文件,并且构建一个Service实现我们定义的AIDL接口(AidlTestService.java),并且在manifest中注册这个服务
2
在其他的进程,需要使用AIDL的地方,调用bindService开启这个服务,然后在ServiceConnection中处理返回的IBinder对象
//开启服务
bindService(service, mConnection, Context.BIND_AUTO_CREATE);
//处理IBinder对象
IAIDLTest mService = null
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IAIDLTest.Stub.asInterface(service)
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null
}
};
3
说完了用法,下面来解析
首先在bindService中,获得我们在AidlTestService.java(aidl实现类)中的onBind
方法中返回的IBinder
实现类一般写法如下
public class AidlTestService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
IAIDLTest.Stub mBinder = new IAIDLTest.Stub() {
//do something
};
}
4
然后在ServiceConnection中,处理上面返回的IBinder
mService = IAIDLTest.Stub.asInterface(service)
看内部类Stub中的方法asInterface
public static com.demo.tianyl.demo.IAIDLTest asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.demo.tianyl.demo.IAIDLTest))) {
return ((com.demo.tianyl.demo.IAIDLTest) iin);
}
return new com.demo.tianyl.demo.IAIDLTest.Stub.Proxy(obj);
}
asInterface方法会根据当前的进程还决定返回Stub或者Proxy
- 如果当前进程和AIDL定义的进程相同,就返回Stub的实现类(AidlTestService.java)
- 如果当前进程和AIDL定义的进程不同,就返回Proxy
所以我们可以猜到,AidlTestService.java是Stub的真实实现类,Proxy是Stub在其他进程的代理实现类
5.4 Stub和Proxy的通信
说完了Stub和Proxy的关系,再说说它们是如何通信的
既然Proxy是Stub在其他进程的实现类,那么其他进程在调用AIDL接口中的方法时,肯定是通过Proxy进行的,例如一个求和方法add
1
Proxy中的方法如下
@Override
public int add(int x, int y) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这个方法主要做了2件事
- 序列化参数x和y
- 调用mRemote.transact方法
其中Stub.TRANSACTION_add是定义的一个标准常量,用于确定调用的是哪个方法
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
2
从mRemote的transact,会回调到Stub的onTransact,在这个方法中,会有
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
在TRANSACTION_add分支中,会将参数反序列化,然后调用this.add(_arg0, _arg1),就是它的实现类,然后将结果回传回去
6 总结
关于AIDL和Binder的相关描述,到此就结束了,因为考虑到已经有不少源码分析的博客,所以这里就较少的涉及源码方面,想必开篇的问题,大家心中已经有答案了吧