AIDL(Android Interface Define Language)文件分析

什么是AIDL文件?

AIDL文件是Android接口定义语言(Android Interface Definition Language)的缩写。它是一种用于描述Android应用程序组件之间通信接口的XML文件。

如何使用AIDL文件?

在Android应用程序中,AIDL文件用于定义在不同进程之间通信的接口。通过定义接口,应用程序的不同组件可以相互通信,从而实现信息共享和数据传输。

在使用AIDL文件时,您需要遵循以下步骤:

  1. 创建一个AIDL文件,用于定义接口。
  2. 实现AIDL接口。
  3. 在应用程序中使用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;
}

根据以上源码,拆解一些比较重要的部分

  1. 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本身

  2. 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,说明是一个默认实现,返回的都是默认值

  3. 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是否死亡呢?

  4. 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 接口中为每个方法定义一个事务代码,可以帮助客户端和服务端正确地匹配请求和响应。

    也就是说这个常量的含义是:每个常量都对应的一个方法,代表可以标识符,可以通过标识符去请求对应的方法

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值