在上一篇文章中讲了如何使用AIDL来进行跨进程通信,还没有看过的可以先看看《Binder机制-AIDL的使用》。
继续使用上一篇文章中的示例,我们来看看aidl文件生成的java类。
package com.aidl.server;
public interface ILoginManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.aidl.server.ILoginManager {
private static final java.lang.String DESCRIPTOR = "com.aidl.server.ILoginManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.aidl.server.ILoginManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aidl.server.ILoginManager))) {
return ((com.aidl.server.ILoginManager) iin);
}
return new com.aidl.server.ILoginManager.Stub.Proxy(obj);
}
a
@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_login: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
com.aidl.server.LoginCallback _arg2;
_arg2 = com.aidl.server.LoginCallback.Stub.asInterface(data.readStrongBinder());
this.login(_arg0, _arg1, _arg2);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.aidl.server.ILoginManager {
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 login(java.lang.String name, java.lang.String pwd, com.aidl.server.LoginCallback callback) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeString(pwd);
_data.writeStrongBinder((((callback != null)) ? (callback.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void login(java.lang.String name, java.lang.String pwd, com.aidl.server.LoginCallback callback) throws android.os.RemoteException;
}
该类的大致有如下几个特点:
asInterface(android.os.IBinder)
、asBinder()
、onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
我们先看看Stub的构造方法做了什么
//Stub
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
//Binder
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
在Stub的构造方法中,调用了父类的attachInterface()方法,在该方法内部,实际上就保存了stub对象本身和一个DESCRIPTOR描述符。
我们在client绑定远程服务后,会调用Stub.asInterface(IBinder obj)
来将一个IBinder对象转为自动生成的接口对象。
public static com.aidl.server.ILoginManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aidl.server.ILoginManager))) {
return ((com.aidl.server.ILoginManager) iin);
}
return new com.aidl.server.ILoginManager.Stub.Proxy(obj);
}
在该方法中会首先调用queryLocalInterface来查询本地进程是否存在一个binder对象,如果存在就返回,不存在就返回一个proxy对象。实际上,如果client和server在同一个进程,那么就直接返回了该stub对象了,我们在server返回的IBinder对象就是一个Stub对象;如果是在不同的进程,那么返回一个proxy对象,在该proxy对象中会持有这个IBinder对象。
其实如果是跨进程,在我们bindService成功后返回的IBinder实际上是一个BinderProxy对象,BinderProxy是Binder的内部类,它也实现了IBinder接口,所以我们在调用asInterface()的时候,在其内部obj.queryLocalInterface(DESCRIPTOR)实际返回null。
final class BinderProxy implements IBinder {
// Assume the process-wide default value when created
volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
public native boolean pingBinder();
public native boolean isBinderAlive();
public IInterface queryLocalInterface(String descriptor) {
return null;
}
...
}
可以看到在BinderProxy中,queryLocalInterface()返回的就是null。所以通过asInterface()后我们拿到了一个Stub$Proxy对象。
这里又出现了一个BinderProxy类,从这里我们能够看到Binder的一些身影。实际上android中的跨进程通信在很多地方都是通过Binder驱动来实现的,Binder工作在内核区,在client进程中,Binder驱动返回给我们的并不是一个Binder对象,而是一个BinderProxy对象,我们并不能真正的拿到server端的binder对象,至于Binder是怎么工作的,因为比较复杂,而且涉及到framework的native层,binder是用c和c++实现的,所以这里就先不讲Binder的实现机制了。
继续上面的话题,在client端我们拿到的是一个Stub$Proxy对象,我们来看看Proxy类中的内容:
@Override
public void login(java.lang.String name, java.lang.String pwd, com.aidl.server.LoginCallback callback) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeString(pwd);
_data.writeStrongBinder((((callback != null)) ? (callback.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
Proxy类同样实现了自动生成的接口。在实现的方法中,会先创建一个_data
和_reply
对象。这两个对象都是Parcel类型的,一个用来传递跨进程中的方法参数,一个用来接收方法的返回值。
DESCRIPTOR
。在这里我们先写入了一个DESCRIPTOR
标识符,它是IDE自动生成的,默认就是生成的接口的全名,使用该DESCRIPTOR
会和server端的IBinder对象进行匹配,如果匹配不到我们后续的操作就无法进行,所以要求我们在client端的aidl文件的路径要和server端一致就是这个原因。
可以看到proxy也只是组装了数据而已,它实际上调用的还是IBinder对象的方法,此处的IBinder对象是一个BinderProxy类型的。transact的具体实现是在framework的native中,是用c++实现的,代码在frameworks\native\libs\binder\IPCThreadState.cpp
中,其中也有一个transact()方法。
通过在proxy中调用BinderProxy的transact()方法,最终Binder驱动会回调到Stub类中的onTransact()
方法。我们可以发现transact()方法和onTransact()是一一对应的。在transact()有固定的4个参数:
static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
一般我们不用关心它,server端和client端针对同一个方法生成的ID都是一样的,格式通常都是TRANSACTION_
+方法名
。接着我们看onTransact()的实现,它会被Binder驱动回调:
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_login: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
com.aidl.server.LoginCallback _arg2;
_arg2 = com.aidl.server.LoginCallback.Stub.asInterface(data.readStrongBinder());
this.login(_arg0, _arg1, _arg2);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
在switch中通过code来判断执行响应的代码,最终都会在server端执行我们在aidl中声明的方法。在这里我们可以看到从data中读取我们在client中传递过来的参数,然后调用具体的aidl中声明的方法,然后在reply中写入结果并返回。
在上一篇中我们提到了定向tag这个东西,使用不同的tag(int、out、inout),我们生成的代码会有一些不同,这里我们从源码来看看它们的不同点。
这里我们重新定义一个aidl问价,内容如下所示:
// ITest.aidl
package com.aidl.server;
import java.util.List;
interface ITest {
String getInfoIn(in List info);
String getInfoOut(out List info);
String getInfoInOut(inout List info);
}
生成的ITest.java如下
package com.aidl.server;
public interface ITest extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.aidl.server.ITest {
private static final java.lang.String DESCRIPTOR = "com.aidl.server.ITest";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.aidl.server.ITest asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aidl.server.ITest))) {
return ((com.aidl.server.ITest) iin);
}
return new com.aidl.server.ITest.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_getInfoIn: {
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
_arg0 = data.createStringArrayList();
java.lang.String _result = this.getInfoIn(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getInfoOut: {
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
_arg0 = new java.util.ArrayList();
java.lang.String _result = this.getInfoOut(_arg0);
reply.writeNoException();
reply.writeString(_result);
reply.writeStringList(_arg0);
return true;
}
case TRANSACTION_getInfoInOut: {
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
_arg0 = data.createStringArrayList();
java.lang.String _result = this.getInfoInOut(_arg0);
reply.writeNoException();
reply.writeString(_result);
reply.writeStringList(_arg0);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.aidl.server.ITest {
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 java.lang.String getInfoIn(java.util.List info) 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);
_data.writeStringList(info);
mRemote.transact(Stub.TRANSACTION_getInfoIn, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.lang.String getInfoOut(java.util.List info) 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);
mRemote.transact(Stub.TRANSACTION_getInfoOut, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
_reply.readStringList(info);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.lang.String getInfoInOut(java.util.List info) 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);
_data.writeStringList(info);
mRemote.transact(Stub.TRANSACTION_getInfoInOut, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
_reply.readStringList(info);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getInfoIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getInfoOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getInfoInOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public java.lang.String getInfoIn(java.util.List info) throws android.os.RemoteException;
public java.lang.String getInfoOut(java.util.List info) throws android.os.RemoteException;
public java.lang.String getInfoInOut(java.util.List info) throws android.os.RemoteException;
}
有了之前的分析,这里再看这些生成的代码就很简单了,我们还是从Proxy这个类说起。
先看getInfoIn()
public java.lang.String getInfoIn(java.util.List info) 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);
_data.writeStringList(info);
mRemote.transact(Stub.TRANSACTION_getInfoIn, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
老样子,先通过_data写入方法参数,然后调用IBinder.transact()方法,这个时候client调用该方法的线程会挂起,直到有结果返回,然后从_reply获取返回值。这里需要注意一点:在通过_data写入参数的时候,直接就将方法中的参数写入到了_data中了。
与之对应onTransact()部分:
case TRANSACTION_getInfoIn: {
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
_arg0 = data.createStringArrayList();
java.lang.String _result = this.getInfoIn(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
从data中读取方法参数,然后调用getInfoIn()方法,最后通过reply返回结果。
再看getInfoOut()
public java.lang.String getInfoInOut(java.util.List info) 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);
_data.writeStringList(info);
mRemote.transact(Stub.TRANSACTION_getInfoInOut, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
_reply.readStringList(info);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这里可以看到和getInfoIn()
有一点区别,就是在获取返回值的时候,如果定向tag是out的话,我们还会通过_reply向我们的方法参数对象上写入一些返回数据。
再来看看与之对应的onTransact()部分:
case TRANSACTION_getInfoOut: {
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
_arg0 = new java.util.ArrayList();
java.lang.String _result = this.getInfoOut(_arg0);
reply.writeNoException();
reply.writeString(_result);
reply.writeStringList(_arg0);
return true;
}
这里就和定向tag为in的时候大不一样了,这里的方法参数是server端直接new出来的一个空对象,并没有使用client传过来的参数,在server端处理完后会将new出来的这个参数通过reply返回给client。
这就和我们在上一篇说的一样,重复一遍这个结论:在使用out定向tag的时候,我们在实现server端的接口方法中,方法参数中的数据总是空的,原因就在这里,server端并没有使用client传过来的数据,而是自己new的一个新对象,里面没有数据。在处理返回数据时server会将这个自己创建的参数给返回。
最后再来看看getInfoInout()
方法:
public java.lang.String getInfoInOut(java.util.List info) 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);
_data.writeStringList(info);
mRemote.transact(Stub.TRANSACTION_getInfoInOut, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
//此处会更新方法参数
_reply.readStringList(info);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
case TRANSACTION_getInfoInOut: {
data.enforceInterface(DESCRIPTOR);
java.util.List _arg0;
//使用的是client传递过来的参数对象
_arg0 = data.createStringArrayList();
java.lang.String _result = this.getInfoInOut(_arg0);
reply.writeNoException();
reply.writeString(_result);
//将方法参数写回client端
reply.writeStringList(_arg0);
return true;
}
可以看到inout方式就是in和out的结合,在client端也会从reply中读取数据写入方法参数对象中,在server端用的是client传递过来的参数对象,同时也会将该参数对象写回client端。
对in、out、inout做一个总结: