什么是AIDL文件?
AIDL文件是Android接口定义语言(Android Interface Definition Language)的缩写。它是一种用于描述Android应用程序组件之间通信接口的XML文件。
如何使用AIDL文件?
在Android应用程序中,AIDL文件用于定义在不同进程之间通信的接口。通过定义接口,应用程序的不同组件可以相互通信,从而实现信息共享和数据传输。
在使用AIDL文件时,您需要遵循以下步骤:
- 创建一个AIDL文件,用于定义接口。
- 实现AIDL接口。
- 在应用程序中使用AIDL接口实现不同组件之间的通信。
AIDL文件示例
以下是一个简单的AIDL文件示例,用于定义一个计算器接口:
// ICalculator.aidl
package com.example.calculator;
interface ICalculator {
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);
}
在上面的示例中,我们定义了一个名为“ICalculator”的AIDL接口,它包含了四个方法:add、subtract、multiply和divide。这些方法分别用于执行加法、减法、乘法和除法。
源码解析
package com.example.myapplication;
public interface ICalculator extends android.os.IInterface
{
public static class Default implements com.example.myapplication.ICalculator
{
...
}
public static abstract class Stub extends android.os.Binder implements com.example.myapplication.ICalculator
{
private static final java.lang.String DESCRIPTOR = "com.example.myapplication.ICalculator";
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.myapplication.ICalculator asInterface(android.os.IBinder 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
{
...
}
private static class Proxy implements com.example.myapplication.ICalculator
{
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 add(int a, int b) throws android.os.RemoteException
{
...
}
@Override public int subtract(int a, int b) throws android.os.RemoteException
{
...
}
@Override public int multiply(int a, int b) throws android.os.RemoteException
{
...
}
@Override public int divide(int a, int b) throws android.os.RemoteException
{
...
}
public static com.example.myapplication.ICalculator sDefaultImpl;
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_subtract = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_multiply = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_divide = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
public static boolean setDefaultImpl(com.example.myapplication.ICalculator impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.myapplication.ICalculator getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public int add(int a, int b) throws android.os.RemoteException;
public int subtract(int a, int b) throws android.os.RemoteException;
public int multiply(int a, int b) throws android.os.RemoteException;
public int divide(int a, int b) throws android.os.RemoteException;
}
根据以上源码,拆解一些比较重要的部分
-
public interface ICalculator extends android.os.IInterface
android.os.IInterface
是一个通用接口,它提供了在 Android 中跨进程通信的方法。它定义了一些方法,允许在进程之间发送和接收数据。在 AIDL 文件的上下文中,该接口被扩展以定义可以跨不同进程调用的方法。看看它的代码package android.os; public interface IInterface { IBinder asBinder(); }
就是一个IBinder对象方法,IBinder是描述两个进程之间的抽象通信通道的接口。实现AIDL接口时,必须实现 asBinder() 方法,因为它用于获取表示AIDL接口的底层IBinder对象。也就是说这个IBinder就是Service(因为Service将要实现这个接口),客户端会拿到IBinder对象,通过转换可以拿到Service本身
-
public static class Default implements com.example.myapplication.ICalculator
ICalculator就是AIDL文件,所以这个这个类就是实现接口里所有的方法
public static class Default implements com.example.myapplication.ICalculator { @Override public int add(int a, int b) throws android.os.RemoteException { return 0; } @Override public int subtract(int a, int b) throws android.os.RemoteException { return 0; } @Override public int multiply(int a, int b) throws android.os.RemoteException { return 0; } @Override public int divide(int a, int b) throws android.os.RemoteException { return 0; } @Override public android.os.IBinder asBinder() { return null; } }
因为ICalculator继承了
android.os.IInterface
所以会有 asBinder()方法,可以看到类名就叫Default,说明是一个默认实现,返回的都是默认值 -
public static abstract class Stub extends android.os.Binder
这个Stub类非常重要,也是核心类,这里直接分析一下
android.os.Binder
类,看下里面具体的方法-
public class Binder implements IBinder
实现了IBinder接口 -
public static final native int getCallingPid();
这个方法的作用是获取正在调用当前方法的进程ID(PID),以便应用程序可以根据需要对该进程进行操作。 -
public static final native int getCallingUid();
它属于android.os.Binder
类。它的具体作用是获取调用当前方法的进程的用户ID(User ID)。可以进行权限验证和安全性检查,在跨进程通信或使用Binder服务时,可以通过比较调用方的用户ID和预期的用户ID来确保调用方是否具有足够的权限执行特定操作。 -
public static final int getCallingUidOrThrow()
它的具体作用是获取调用当前方法的进程的用户ID(User ID),如果无法获取到用户ID,则抛出SecurityException异常。 -
public static final UserHandle getCallingUserHandle()
它的具体作用是获取调用当前方法的进程的用户句柄(UserHandle),getCallingUserHandle()
方法返回的是一个UserHandle
对象,它包含了用户的标识信息,如用户ID等。可以通过UserHandle
对象的其他方法来进一步获取用户的信息或进行操作,比如获取用户ID、检查用户是否是当前用户 -
public static final native long clearCallingIdentity();
和public static final native void restoreCallingIdentity(long var0);
1.它的具体作用是清除当前调用线程的身份标识符(Identity),并返回一个表示清除前身份的标识符(也就是回到默认身份)。
2.在Android系统中,每个线程都有一个身份标识符,用于标识线程的执行者或调用者。身份标识符可以代表应用程序的身份、权限或其他上下文相关的信息。
3.通过调用
clearCallingIdentity()
方法,可以清除当前调用线程的身份标识符,将其恢复为默认状态,以避免在特定的上下文中持有错误的身份。4.这个方法通常与
restoreCallingIdentity(long token)
方法一起使用,restoreCallingIdentity()
方法可以用于恢复之前清除的身份标识符,将其设置回调用clearCallingIdentity()
方法之前的状态。通过这种方式,可以确保在特定的上下文中使用正确的身份标识符。 -
public static final native void flushPendingCommands();
当使用AIDL进行进程间通信时,客户端和服务器端之间的通信是通过Binder进行的。在某些情况下,特别是在跨进程调用时,客户端发出的命令可能会被挂起并排队等待执行。这种情况下,
flushPendingCommands()
方法可以用来强制刷新(提交)这些被挂起的命令,以便它们能够立即开始执行。 -
public static final void joinThreadPool()
将当前线程加入到Binder线程池中,等待执行 -
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor)
这个方法用于将接口的实例(owner)与对应的描述符(descriptor)进行绑定,owner 参数代表 AIDL 接口的实例,即实现了该接口的类的对象,descriptor 参数是一个字符串,用于描述接口的标识符。它通常是接口的全名,包括包名和类名。
这个方法的作用是在代理类(Proxy)中将接口实例与描述符进行关联,代理类是在 IPC 过程中用于跨进程通信的代理对象,它负责将方法调用传递到实际的实现类。简单点说:将ICalculator.aidl
文件的实例(也就是Service)和代理类进行关联,一般客户端就是拿到代理类(可以理解为ICalculator.aidl
的副本),然后来和服务端交互 -
public String getInterfaceDescriptor()
在代理类(Proxy)中,可以通过调用getInterfaceDescriptor()
方法获取与该接口关联的描述符(也就是之前绑定的包名) -
public boolean pingBinder()
当一个客户端与远程服务绑定时,会通过Binder对象进行通信。pingBinder()
方法被用于检测远程Binder对象的状态。调用该方法将向远程Binder发送一个ping请求,然后等待返回结果。如果远程Binder对象正常运行且可用,则pingBinder()
方法返回true
,表示与远程服务的通信正常。如果远程Binder对象不可用,例如由于服务崩溃或连接中断,那么该方法将返回false
,表示与远程服务的通信异常
通过调用pingBinder()
方法,客户端可以检查与远程服务的通信是否可靠,以便在必要时采取适当的处理措施,比如重新绑定服务或显示错误提示给用户 -
public boolean isBinderAlive()
用于检查本地Binder对象的状态,是否仍然存活;isBinderAlive()方法不会检查远程进程是否存活,而只是检查本地进程是否还持有该Binder的引用,而pingBinder用于发送ping请求,检查服务端或客户端是否可以回复(检查连通性),检查两端都是否正常 -
public IInterface queryLocalInterface(@NonNull String descriptor)
通过指定的描述符(descriptor)查找本地进程中与之匹配的接口实例(Binder代理) -
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
用于处理客户端向服务端发送的跨进程调用请求,接收跨进程调用请求:当客户端通过 Binder 对象向服务端发起跨进程调用请求时,该方法会被调用。它接收请求的参数,包括调用的方法代码(code)、传递的数据(parcel)、回复的结果(parcel)以及附加的标志(flags)
1.code
:方法代码,作用不详
2.data
:客户端发来数据实体(用于网络传输的数据都必须实例化)
3.reply
:服务端回复客户端的数据实体
4.flags
:标志位,作用不详
onTransact
方法返回一个布尔值,用于表示跨进程调用的状态。如果调用成功并顺利执行了相应的方法逻辑,它应返回true
。否则,如果服务端处理出现问题或者调用的方法代码无效,它应返回false
,表示调用失败。所以我们可以在服务端(Service)重写这个方法,做身份校验,若身份校验不通过可以返回false,代表通信失败。【注意这个方法是运行在服务端的】 -
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
用于客户端向服务端发起跨进程调用请求,注意是客户端发起请求,客户端通过调用transact
方法来组装跨进程调用的请求参数。请求参数包括方法代码(code)、传递的数据(parcel)、回复结果的 parcel(reply)以及附加的标志(flags),一旦请求参数组装完成,transact
方法将会发送跨进程调用请求给服务端的 Binder 对象,然后等待服务端处理并获取回复,transact
方法返回一个布尔值,用于表示跨进程调用的状态。如果调用成功并接收到有效的回复,它应返回true
。否则,如果出现调用失败或回复无效的情况,它应返回false
【注意这个方法是运行在客户端的】,用于客户端发起请求到服务端的(onTransact
),会现在客户端组装参数和实体数据,传递到服务端,并等待服务端的回复(也有可能服务端没有数据回复) -
public void linkToDeath(@NonNull DeathRecipient recipient, int flags)
注册Binder对象的死亡通知,该方法用于向 Binder 对象注册死亡通知(Death Notification)。通过调用linkToDeath
方法,如果远程服务的 Binder 对象死亡时,客户端会收到通知,可以做出重连机制或者做释放内存动作 -
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags)
该方法用于取消注册之前注册的死亡通知。如果不再需要接收远程对象的死亡通知,客户端可以调用unlinkToDeath
方法,以取消注册 -
注意以上的死亡通知,不是说我确保客户端死亡了再手动调用这个方法,去通知客户端,而是在绑定的时候就调用这个方法,到时候客户端死亡了,自然会发送通知
-
思考题:以上都是客户端监听远程Binder(服务端)是否死亡,能否实现服务端监听客户端的Binder是否死亡呢?
-
-
private static class Proxy implements com.example.myapplication.ICalculator
这个代理类属于
Stub
的子类,主要就是客户端的代理类,实现了ICalculator.aidl
接口的所有方法,如下方法:private android.os.IBinder mRemote;
Proxy(android.os.IBinderremote)
public android.os.IBinder asBinder(){return mRemote;}
public java.lang.String getInterfaceDescriptor()
@Override public int add(int a, int b)
@Override public int subtract(int a, int b)
@Override public int multiply(int a, int b)
@Override public int divide(int a, int b)
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_subtract = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_multiply = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_divide = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
可以看到自定义的接口方法,在这里都进行实现了,这里主要关注这些常量值得意义,
TRANSACTION
代表基准值,它的值通常是android.os.IBinder.FIRST_CALL_TRANSACTION
加上一个偏移量。在 AIDL 接口中,每个方法都会被分配一个唯一的事务代码。这些代码用于在底层进行通信时标识特定的方法请求。通过在 AIDL 接口中为每个方法定义一个事务代码,可以帮助客户端和服务端正确地匹配请求和响应。
也就是说这个常量的含义是:每个常量都对应的一个方法,代表可以标识符,可以通过标识符去请求对应的方法