aidl是android中非常重要的进程间通信的方式,底层还是用的binder来实现的。今天就重点讲解一下aidl的使用和原理;
基础使用
aidl本质上也是采用的c/s架构,首先创建一个server端:
server端
- 1.右键生成aidl文件
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String getName();
void setName(String name);
}
basicTypes这个可以删掉的。这个方法是介绍了基础的用法,可以用哪些变量;后面就可以自定义方法了。
- 2.创建service
@Override
public IBinder onBind(Intent intent) {
return new Like();
}
class Like extends IMyAidlInterface.Stub{
@Override
public String getName() throws RemoteException {
return "i'm yours";
}
@Override
public void setName(String name) throws RemoteException {
}
}
创建一个内部类,继承自aidl.stub。然后重写方法;
然后在onBind方法中return 这个内部类的对象。
Client端
client端的写法就很简单了。
Intent intent= new Intent(action);
intent.setPackage(packagename);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
就是一个bindservice的过程;这样两个app就能通信啦。怎么样,是不是很简单,接下来讲下实现的原理;
原理分析
要知道原理首先肯定得知道在编译aidl文件时,发生了什么。观察看目录知道其实是生成了一个同名的java文件,下面我们来研究这个java文件里都有啥。
/**
public static class Default implements com.qikun.demo.IMyAidlInterface {
@Override
public void setName(java.lang.String name) throws android.os.RemoteException {
}
@Override
public java.lang.String getName() throws android.os.RemoteException {
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
首先生成了一个默认的实现了此接口的类,叫default;
重点看stub类,此类实现了IMyAidlInterface的接口并继承了binder。
public static com.qikun.demo.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.qikun.demo.IMyAidlInterface))) {
return ((com.qikun.demo.IMyAidlInterface) iin);
}
return new com.qikun.demo.IMyAidlInterface.Stub.Proxy(obj);
}
将服务端的ibinder对象转化为客户端所需要的接口对象。
如果有必要的话,产生一个代理对象Proxy。否则直接查询本地的对象,就解决了同一个进程里使用aidl的困境)
来分析一下这个proxy代理对象是怎么回事:
首先,proxy构造需要传入一个ibinder对象;
@Override
public java.lang.String getName() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getName();
}
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
一个data,一个reply,都是parcel对象。注意最核心的代码:
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
mRemote其实就是proxy初始化的时候传入进来的binder对象。transact是什么方法呢?
/**
* Perform a generic operation with the object.
*
* @param code The action to perform. This should
* be a number between {@link #FIRST_CALL_TRANSACTION} and
* {@link #LAST_CALL_TRANSACTION}.
* @param data Marshalled data to send to the target. Must not be null.
* If you are not sending any data, you must create an empty Parcel
* that is given here.
* @param reply Marshalled data to be received from the target. May be
* null if you are not interested in the return value.
* @param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
*
* @return Returns the result from {@link Binder#onTransact}. A successful call
* generally returns true; false generally means the transaction code was not
* understood.
*/
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;
这里就从官方文档注释大概知道意思:
- 1.code表示要执行什么动作;
- 2.data和reply就是一个传入值一个返回值;
- 3.flag只有0和flag_oneway,0表示client需要等待server返回,而flag_oneway表示这是一个单项的活动,无需等待。所以参数为0时表示可能会有阻塞,这里要多注意.
okay,让我们再回到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_setName: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(descriptor);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
可以看到stub根据不同的code去执行不同的方法;
小结
通过以上的源码分析能够得出一次aidl的IPC(inter-process communication)大致流程:
- a.客户端通过bindService绑定服务端的service
- b.拿到service之后拿到ibinder对象,再通过接口的Stub.asInterface将ibinder对象转化为接口对象。
- c.拿到接口对象之后调用接口的api,api里通过binder.transact方法调起远程通信,如果flag是0,等待结果回来。
- d.远程接收到后,通过写入参数到reply参数中去,然后再调用binder.transact方法返回。
这里再稍微提一下binder机制。因为这个实在是太重要了对于android系统来说。
首先android系统不同进程是不共享空间的,但是内核空间是共享的,因此binder 驱动层肯定是在内核空间的,相当于一个底层中转站的概念。后续将花大篇幅介绍binder。
网友评论