一步一步的,带着疑问,用示例讲解aidl文件。
创建生成aidl文件
路径:src/main/aidl/com/example/aidl/IMyAidlInterface.aidl
package com.example.aidl;
interface IMyAidlInterface {
void test(in int a);
}
编译生成java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /media/fukaiqiang/Disk960/AndroidProject/Test_AIDL/app/src/main/aidl/com/example/aidl/IMyAidlInterface.aidl
*/
package com.example.aidl;
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.aidl.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidl.IMyAidlInterface))) {
return ((com.example.aidl.IMyAidlInterface) iin);
}
return new com.example.aidl.IMyAidlInterface.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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_test: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _result = this.test(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.aidl.IMyAidlInterface {
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 test(int a) 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(a);
mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int test(int a) throws android.os.RemoteException;
}
结构分析
package com.example.aidl;
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.aidl.IMyAidlInterface {
}
public void test(int a) throws android.os.RemoteException;
}
- aidl生成的java文件依然是一个接口文件,它继承了android.os.IInterface,并创建了一个名为Stub的内部抽象类,原来的test方法抛出一个RemoteException异常。
- 先看IInterface的接口文件,内容如下:
package android.os;
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
通过注释了解到,当创建一个新的aidl接口文件的时候,你就需要继承IInterface,并让与IMyAidlInterface关联的Binder对象所在类实现asBinder方法,并返回this,即返回这个Binder对象。所以一个普通类如果实现IMyAidlInterface,需要实现两个方法,一个方法是test,一个方法是asBinder,asBinder这个时候返回null即可,因为根本不需要通过binder进行通信;如果一个继承了Binder的类实现IMyAidlInterface,同样需要实现上面两个方法,而且asBinder需要返回这个Binder对象。
普通类:
public class TestAidl implements IMyAidlInterface{
@Override
public void test(int a) throws RemoteException {
}
@Override
public IBinder asBinder() {
return null;
}
}
继承了Binder的类
class InterTestAild extends Binder implements IMyAidlInterface{
@Override
public void test(int a) throws RemoteException {
}
@Override
public IBinder asBinder() {
return this;
}
}
-
现在看内部抽象类Stub(强调Stub是一个抽象类,必须由其他类继承使用),这个类继承了Binder,并实现了IMyAidlInterface接口,为了更加方便的看清内部类的结构,截图如下:
让我们分析下内部类的结构:
首先:定义了一个静态常量DESCRIPTOR,其值由包名加接口名组成。
其次:在构造方法中调用了attachInterface方法,并传入了两个参数分别是this对象和静态常量DESCRIPTOR。这里的this对象指的是继承了Stub的类的对象。
接着:定义了一个静态方法asInterface,其参数的类型是IBinder接口类型,即实参必须是实现了IBinder接口的对象。
继续:如分析2,实现了接口IInterface的类,需要实现asBinder方法,并返回其对象this。
然后:实现了onTransact方法,一共4个参数,分别是int类型的code,Parcel类型的data,Parcel类型的reply,int类型的flags。
接着:定义了一个静态内部类Proxy,并实现了IMyAidlInterface接口。
最后:定义一个静态常量TRANSACTION_test,其名称组成是TRANSACTION + 下划线 + 方法名,其值是常量FIRST_CALL_TRANSACTION + 0,FIRST_CALL_TRANSACTION = 0x00000001。
详细分析
首先看attachInterface方法。这里涉及到了this,所以需要改写下InterTestAild类,如下:
class InterTestAild extends IMyAidlInterface.Stub {
@Override
public void test(int a) throws RemoteException {
}
@Override
public IBinder asBinder() {
return this;
}
}
让InterTestAild由继承Binder,改为继承IMyAidlInterface.Stub。所以当创建InterTestAild对象的时候,就会调用IMyAidlInterface.Stub类的构造方法,并调用attachInterface方法。这里的this即InterTestAild对象。
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
所以这里的mOwner = InterTestAild对象,mDescriptor即为常量DESCRIPTOR。
接着分析asInterface方法:
/**
* Cast an IBinder object into an com.example.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidl.IMyAidlInterface))) {
return ((com.example.aidl.IMyAidlInterface) iin);
}
return new com.example.aidl.IMyAidlInterface.Stub.Proxy(obj);
}
通过注释,了解到这个方法的作用:将接口IBinder类型转换为IMyAidlInterface接口,如果需要,创建IMyAidlInterface.Stub.Proxy类型的对象。大体逻辑,如果obj == null直接返回,如果iin == null,那就创建com.example.aidl.IMyAidlInterface.Stub.Proxy类的对象,否则强转为IMyAidlInterface类型。
核心的地方就是queryLocalInterface方法,它是在IBinder中定义的。这个时候你要注意了,用AndroidStudio做的跳转是错误的。如果你用AndroidStudio跳转,只会跳转到Binder类的queryLocalInterface方法。如下:
/**
* Use information supplied to attachInterface() to return the
* associated IInterface if it matches the requested
* descriptor.
*/
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
但其实不止Binder类实现了IBinder的queryLocalInterface方法,还有BinderProxy类也实现了IBinder的queryLocalInterface方法(我是怎么发现的,这个后面说)。
/**
* Retrieve a local interface - always null in case of a proxy
*/
public IInterface queryLocalInterface(String descriptor) {
return null;
}
通过注释你会发现BinderProxy中的queryLocalInterface总是会返回null。也就是说如果obj的类型是BinderProxy,那么会返回null,并创建com.example.aidl.IMyAidlInterface.Stub.Proxy类的对象,如果obj的类型是Binder,那么会返回attachInterface方法存储的继承了Binder类的对象(前面有说,这里不详细说)。所以关键就是obj到底是什么类型(后面会说何时是Binder类型,何时是BinderProxy类型)。
下面分析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_test: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.test(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
看到这里,第一反应就是一个aidl接口的方法对应一个code,根据code调用这个方法,方法和code是一一对应的关系。以下是执行这个方法的地方。
this.test(_arg0);
通过data和reply调用的方法,了解到从data中读取数据,把数据写入reply,即data用来传递数据过来,reply用来回调处理后的信息。
最后看Proxy内部类的内部类.
private static class Proxy implements com.example.aidl.IMyAidlInterface {
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 test(int a) 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.writeInt(a);
mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
类Proxy也实现了IMyAidlInterface,自然也有一个test方法。再看构造函数,把传入的IBinder接口类型的对象存储到mRemote变量中。getInterfaceDescriptor方法返回DESCRIPTOR常量。test方法里面,定义了_data和_reply,并把数据写入到_data,从reply中读取数据,并调用了transact方法,注意它是IBinder中定义的,在Binder和BinderProxy中都有实现,所以mRemote是Binder类型,还是BinderProxy类型,调用的transact方法是有区别的,一共有4个参数,分别是code,data,reply,flags。看到这里,你会发现test中的工作和onTransact有关联,前者负责数据的发送,后者负责数据的读取和回调,前者负责回调的读取。也就是说执行Proxy的test方法,最终会调到Stub的onTransact方法。那Proxy的test方法何时执行呢?test方法执行,首先依赖Proxy对象的创建,那最终还是回到了asInterface方法。让我们用示例调试的方法看看。
示例进行信息挖掘
以Activity和Service进行数据传递的方式进行信息挖掘。
public class AidlService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new InterTestAild();
}
static class InterTestAild extends IMyAidlInterface.Stub {
private static final String TAG = "InterTestAild";
@Override
public void test(int a) throws RemoteException {
Log.d(TAG, "InterTestAild test a = " + a);
}
@Override
public IBinder asBinder() {
return this;
}
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: " + service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindService(new Intent(this,AidlService.class),mServiceConnection,BIND_AUTO_CREATE);
}
<service android:name=".AidlService"/>
创建AidlService,InterTestAild作为内部类存在。在Activity里面bindService,接收回调,获取 InterTestAild的类对象。看看这个时候对象的类型:
2022-06-13 14:39:06.199 29443-29443/com.example.aidl D/MainActivity: onServiceConnected: com.example.aidl.AidlService$InterTestAild@d9f0308
实现了IBinder接口的对象的类型是com.example.aidl.AidlService$InterTestAild,也就是说这个时候asInterface传入的类型是com.example.aidl.AidlService$InterTestAild。此时InterTestAild和activity是在同一个进程中。假如在不同的进程中呢?
<service android:name=".AidlService" android:process=":remote"/>
2022-06-13 14:40:06.190 29638-29638/com.example.aidl D/MainActivity: onServiceConnected: android.os.BinderProxy@4c430ab
实现了IBinder接口的对象的类型是android.os.BinderProxy。也就说在同一个进程中,实现IBinder接口的对象的类型是"com.example.aidl.AidlService$InterTestAild",不同进程是android.os.BinderProxy。
所以这个时候我们就可以推断到asInterface方法的参数类型,当同一个进程的时候是com.example.aidl.AidlService$InterTestAild,不同进程的时候android.os.BinderProxy。进而推断出,不同进程的时候会创建com.example.aidl.IMyAidlInterface.Stub.Proxy的对象。再推断出,当相同进程的时候,调用test方法,会回调IMyAidlInterface接口类的test方法,当不同进程的时候,会调用Proxy的test方法。
所以调用test方法的代码,可以这样写,如下:
当在一个进程的时候:
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: " + service);
//same process
try {
((AidlService.InterTestAild)service).test(5);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
当在一个进程或者不同进程的时候:
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: " + service);
//same process and different process
try {
IMyAidlInterface.Stub.asInterface(service).test(5);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
总结
- Stub是用来实现功能的类的父类,而Proxy是提供给外部进程进行功能调用的类。
- 我们把继承了Binder类的类成为Binder类,其对象成为Binder对象。
- 当我们需要创建一个Binder对象的时候,只需要让类继承Stub即可。
- 一个接口方法对应一个code,按顺序,从1开始排序。
- aidl 文件的存在其实就是为我们提供了不差别的访问binder对象的方法,让我们不用关心binder对象和获取动作是在一个进程,还是在不同的进程。
- binder进行数据的传递,需要binder对象,方法名,参数,异步还是同步4个要素。
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
你现在再看看这段代码,是不是比之前要明白许多了!、
到此aidl的分析完毕。
建议:
学习binder,关注核心通信逻辑,忽视面向对象的业务逻辑,忽视术语(本地,远程,对端等等)。
网友评论