接上一篇文章 android进程间通信Binder(一),我们这里来看看aidl文件到底做了什么
ITest.aidl
package com.example.lenovo.testbinder;
import com.example.lenovo.bean.Student;
interface ITest {
int getAge(int num);
String getName();
Student getStudent();
}
当我们定义了一个aidl文件,android 会自动针对这个aidl文件生成一个ITest.java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\alldemo\\TestBinder\\app\\src\\main\\aidl\\com\\example\\lenovo\\testbinder\\ITest.aidl
*/
package com.example.lenovo.testbinder;
public interface ITest extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.lenovo.testbinder.ITest {
private static final java.lang.String DESCRIPTOR = "com.example.lenovo.testbinder.ITest";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.lenovo.testbinder.ITest interface,
* generating a proxy if needed.
*/
public static com.example.lenovo.testbinder.ITest asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.lenovo.testbinder.ITest))) {
return ((com.example.lenovo.testbinder.ITest) iin);
}
return new com.example.lenovo.testbinder.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_getAge: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _result = this.getAge(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getStudent: {
data.enforceInterface(DESCRIPTOR);
com.example.lenovo.bean.Student _result = this.getStudent();
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.lenovo.testbinder.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 int getAge(int num) 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(num);
mRemote.transact(Stub.TRANSACTION_getAge, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@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);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.example.lenovo.bean.Student getStudent() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.lenovo.bean.Student _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.example.lenovo.bean.Student.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public int getAge(int num) throws android.os.RemoteException;
public java.lang.String getName() throws android.os.RemoteException;
public com.example.lenovo.bean.Student getStudent() throws android.os.RemoteException;
}
我们来分析一下这个java文件个内容,分析完了差不多就知道这个是怎么做的了
一步步来:
我们可以看到,ITest类中里面构建了 一个内部类,Stub,继承自Binder,并且实现了ITest:
构造方法:
private static final java.lang.String DESCRIPTOR = "com.example.lenovo.testbinder.ITest";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
Binder.java源码
private IInterface mOwner;
private String mDescriptor;
/**
* Convenience method for associating a specific interface with the Binder.
* After calling, queryLocalInterface() will be implemented for you
* to return the given owner IInterface when the corresponding
* descriptor is requested.
*/
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
在构造方法中,把自己this和一个
DESCRIPTOR给了自己,(个人猜想,后续再寻找这个Binder对象的时候,就是根据这个DESCRIPTOR来找,找到之后,就把 mOwner返回回去,这个mOwner就是引用
)。这个DESCRIPTOR就是上文提到的包名+文件名的组合,所以在AIDL的使用的时候要求 服务端和客户端的AIDL的包名和文件名完全一致的原因,如果不一样,那么在使用和查找的时候,就没有办法去一一对应了。
public static com.example.lenovo.testbinder.ITest asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.lenovo.testbinder.ITest))) {
return ((com.example.lenovo.testbinder.ITest) iin);
}
return new com.example.lenovo.testbinder.ITest.Stub.Proxy(obj);
}
asInterface(IBinder)方法,在客户端中,调用的就是ITest.Stub.asInterface(service)来得到的一个ITest对象,实际上调用的就是这个地方,它返回的是一个ITest.Stub.Proxy(obj),从名字上看是一个ITest的代理对象,从上面的代码中,看到了Proxy实现了ITest,那么必然的,就需要实现ITest的几个接口。
我们在Client中调用的方法就调用在这个地方来了,我截取其中一个来看看具体的实现方法中到底做了什么:
@Override
public int getAge(int num) 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(num);
mRemote.transact(Stub.TRANSACTION_getAge, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
这一块是针对Client端的数据构成:
第3、 4行定义了两个序列化对象,一个_data是用来封装Client这边传递过去的数据,一个_reply是用来封装Server端返回的数据;
第7行_data.writeInterfaceToken(DESCRIPTOR),我认为是指定binder驱动中需要把数据传递的目标,这个目标的DESCRIPTOR是“com.example.lenovo.testbinder.ITest”,这样前面构造方法中设置进去的值就有了用武之地,就避免找错了Binder引用。
第8行将数据写入到_data中
第9行就是去操作binder对象了,这里指定了操作的是哪个方法,并且将_data,_reply数据都传递过去,完成了数据的传送。
第11行从_reply中获取到服务端返回的数据值。
Binder.java源码transact:
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
从源码中可以看到,调用了这个方法之后,其实最终哈市调用到了onTransact方法,来看看这个方法都做了什么
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
......
case TRANSACTION_getAge: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _result = this.getAge(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
........
}
首先去确认了DESCRIPTOR,然后从_data中获取到从Client中传递过来的数据_arg0,调用this.getAge(_arg0),服务端,是TestBinder继承了ITest.Stub,所以必然是去TestBinder中找到getAge()方法,拿到返回值之后又写入了_reply中,并且return true;
我们对比客户端掌握的的transact() 和 Server端掌握的onTransact()方法,所有的数据的检查和读取,以及写入都是一一对应的,服务端writeNoExtion(),writeInt(),所以在客户端中,有了readException(),readInt()操作。
既然知道了这其中的原理,其实我们完全可以舍弃了AIDL文件来实现自己的Binder类做数据的传递和读取。
------------------------------------------------------------------------------分割线------------------------------------------------------
下面就是我自己写的Binder的实现类。
服务端:
class TestCustomBinder extends Binder implements ICustom {
private static TestCustomBinder mBinder;
public static TestCustomBinder instance() {
if (mBinder == null) {
mBinder = new TestCustomBinder();
}
return mBinder;
}
// 这个顺序是按照aidl文件中提供的方法的顺序来定义顺序的,统一从 0x01 开始
private static final int GET_AGE = 0x01;
private static final int GET_NAME = GET_AGE + 1;
private static final int GET_STUDENT = GET_AGE + 2;
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case GET_AGE:
data.enforceInterface("com.example.lenovo.testbinder.MMMMMM");
int arg0 = data.readInt();
reply.writeNoException();
reply.writeInt(getCustomAge(arg0));
return true;
case GET_NAME:
data.enforceInterface("com.example.lenovo.testbinder.MMMMMM");
reply.writeNoException();
reply.writeString(getCustomName());
return true;
case GET_STUDENT:
data.enforceInterface("com.example.lenovo.testbinder.MMMMMM");
reply.writeNoException();
Student std = getCustomStudent();
reply.writeInt(1);
// 使用writeToParcel时,是直接写入的name和age数据
// 推荐使用这种方式,在反解Parcel数据时,不需要多解释一步classname
// reply.writeString(std.getClass().getName());
std.writeToParcel(reply, 0);
// 使用writeParcelable时,源码会先写入Student的类名(com.example.lenovo.bean.Student)
// 然后调用 writeToParcel 写入数据
// reply.writeParcelable(std, flags);
return true;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public int getCustomAge(int number) {
return 4 * number;
}
@Override
public String getCustomName() {
return "HENSUIYI";
}
@Override
public Student getCustomStudent() {
return new Student("wangwu", 9);
}
}
interface ICustom {
int getCustomAge(int number);
String getCustomName();
Student getCustomStudent();
}
Parcel.java源码
/**
* Flatten the name of the class of the Parcelable and its contents
* into the parcel.
*
* @param p The Parcelable object to be written.
* @param parcelableFlags Contextual flags as per
* {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
*/
public final void writeParcelable(Parcelable p, int parcelableFlags) {
if (p == null) {
writeString(null);
return;
}
writeParcelableCreator(p);
p.writeToParcel(this, parcelableFlags);
}
/** @hide */
public final void writeParcelableCreator(Parcelable p) {
String name = p.getClass().getName();
writeString(name);
}
客户端:
IBinder mTestBinder;
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mTestBinder = service;
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
// 完全可以自定义,只要与服务端定义的DESCRIPTOR一致就好了。注意千万不能重名
_data.writeInterfaceToken("com.example.lenovo.testbinder.MMMMMM");
_data.writeInt(5);
// 需要指定CODE,getAge(int) 的Code 是 0x01
mTestBinder.transact(0x01, _data, _reply, 0);
_reply.readException();
int result = _reply.readInt();
Log.e(TAG, "result = " + result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
用上面这样的写法一样完成了跨进程通信,并且舍弃了AIDL文件,AIDL文件只是一种手段,简化了使用者在使用跨进程通信时的工作量,只要理解了其中的内容,一样可以完成一样的工作。
以上就是我从上层看到的流程分析,至于底层binder的内核机制,后续会去看看到底是如何完成的。