前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Binder 机制】AIDL 分析 ( 分析 AIDL 文件生成的 Java 源文件 | Binder | IBinder | Stub | Proxy )

【Binder 机制】AIDL 分析 ( 分析 AIDL 文件生成的 Java 源文件 | Binder | IBinder | Stub | Proxy )

作者头像
韩曙亮
发布2023-03-29 17:21:10
1.6K0
发布2023-03-29 17:21:10
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

前言

在上一篇博客 【Binder 机制】AIDL 分析 ( 创建 AIDL 文件 | 创建 Parcelable 类 | AIDL 中使用 Parcelable 类 | 编译工程生成 AIDL 对应的Java源文件 ) 创建了 AIDL 文件 , 并编译生成了 AIDL 文件对应的 Java 源文件 , 现在开始分析生成在 " AIDL_Demo\app\build\generated\aidl_source_output_dir\debug\out\kim\hsl\aidl_demo " 目录 中的 " IMyAidlInterface.java " 源文件 ;

一、分析 AIDL 文件生成的 Java 源文件


分析 【Binder 机制】AIDL 分析 ( 创建 AIDL 文件 | 创建 Parcelable 类 | AIDL 中使用 Parcelable 类 | 编译工程生成 AIDL 对应的Java源文件 ) 二、编译工程生成 AIDL 文件对应的 Java 源文件 2、生成的 AIDL 对应 Java 源文件 章节中 , 编译 AIDL 文件生成的源码 ;

1、IMyAidlInterface.java 中的类结构

生成的类是 IMyAidlInterface.java , 继承了 android.os.IInterface 接口 ;

代码语言:javascript
复制
public interface IMyAidlInterface extends android.os.IInterface

其中定义了两个内部类 ,

  • 内部类
1

如下 : IMyAidlInterface 的默认实现 ;

代码语言:javascript
复制
  /** Default implementation for IMyAidlInterface. */
  public static class Default implements kim.hsl.aidl_demo.IMyAidlInterface
  • 内部类
2

如下 : IPC 机制的本地实现 ;

代码语言:javascript
复制
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub 
  	extends android.os.Binder 
  	implements kim.hsl.aidl_demo.IMyAidlInterface

2、DESCRIPTOR 描述符

在 Stub 内部类中 , 定义的常量 DESCRIPTOR 是 AIDL 文件的 " 包名.类名 " , 用于查找 Binder 用的 ;

代码语言:javascript
复制
private static final java.lang.String DESCRIPTOR = "kim.hsl.aidl_demo.IMyAidlInterface";

3、Stub 构造方法

Stub 的构造方法中 , 调用了 BinderattachInterface 方法 , 传入了 AIDL 文件的全类名 ; 作用是将该 AIDL 接口与 Binder 进行关联 ;

代码语言:javascript
复制
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }

4、Stub.asInterface 方法

Stub 中定义了 asInterface 方法 , 该方法的作用是将 android.os.IBinder 对象转为 AIDL 接口对象 ; 传入的 DESCRIPTOR 描述符 , 用于描述用户想要哪个 Binder , android.os.IBinder 对象调用 queryLocalInterface 方法 , 检查本地服务是否存在 ;

  • 如果可以找到本地服务对应的接口 , 可以直接返回本地服务 ;
  • 如果没有找到本地服务 , 就会返回一个 Stub 代理 ;

详细的过程参考下面的代码 :

代码语言:javascript
复制
    /**
     * 将IBinder对象强制转换为kim.hsl.aidl_demo.IMyAidlInterface接口,必要时生成代理。
     */
    public static kim.hsl.aidl_demo.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      // 传入 DESCRIPTOR 描述符 , 用于描述用户想要哪个 Binder
      // android.os.IBinder 对象调用 queryLocalInterface 方法 , 检查本地服务
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      // 如果可以找到本地服务对应的接口 , 可以直接返回本地服务 
      if (((iin!=null)&&(iin instanceof kim.hsl.aidl_demo.IMyAidlInterface))) {
        return ((kim.hsl.aidl_demo.IMyAidlInterface)iin);
      }
      // 如果没有找到本地服务 , 就会返回一个 Stub 代理 
      return new kim.hsl.aidl_demo.IMyAidlInterface.Stub.Proxy(obj);
    }

IBinder 是一个接口 , 其中定义了一堆常量标识符 ;

transact 方法对应 Binder 底层发起 IPC 的请求函数 ;

代码语言:javascript
复制
public interface IBinder {
    /**
     * 对对象执行常规操作。
     * 
     * @param code 要执行的操作。
     * 		这应该是介于{@link #FIRST_CALL_TRANSACTION}和{@link #LAST_CALL_TRANSACTION}之间的数字。
     * @param data 要发送到目标的封送数据。不能为null。
     * 		如果不发送任何数据,则必须创建此处给出的空地块。
     * @param reply 要从目标接收的封送数据。如果您对返回值不感兴趣,则可能为null。
     * @param flags 其他操作标志。正常RPC为0,单向RPC为{@link#FLAG_ONEWAY}。
     *
     * @return 从{@link Binder#onTransact}返回结果。成功的调用通常返回true;false通常表示未理解事务代码。
     */
    public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
        throws RemoteException;
}

5、Stub.onTransact 方法

onTransact 方法用于处理不同的 IPC 请求 , 根据不同的值 , 处理不同的 IPC 请求 ;

要处理的 IPC 请求对应的常量值定义 : 将要调用的方法变成 int 类型的 ID 常量值 , 根据传入的常量值执行相应的方法 , 方便 IPC 跨进程调用 ;

代码语言:javascript
复制
    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_getStudents = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);

TRANSACTION_basicTypes 常量值对应如下 AIDL 方法 :

代码语言:javascript
复制
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

TRANSACTION_addStudent 常量值对应如下 AIDL 方法 :

代码语言:javascript
复制
    /**
     * in 写入, out 输出, inout 写入和输出
     */
    void addStudent(inout Student student);

TRANSACTION_getStudents 常量值对应如下 AIDL 方法 :

代码语言:javascript
复制
    /**
     * 获取 Student 集合
     */
    List<Student> getStudents();

Stub.onTransact 方法代码内容 :

代码语言:javascript
复制
    @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_basicTypes:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          long _arg1;
          _arg1 = data.readLong();
          boolean _arg2;
          _arg2 = (0!=data.readInt());
          float _arg3;
          _arg3 = data.readFloat();
          double _arg4;
          _arg4 = data.readDouble();
          java.lang.String _arg5;
          _arg5 = data.readString();
          this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_addStudent:
        {
          data.enforceInterface(descriptor);
          kim.hsl.aidl_demo.Student _arg0;
          if ((0!=data.readInt())) {
            _arg0 = kim.hsl.aidl_demo.Student.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          this.addStudent(_arg0);
          reply.writeNoException();
          if ((_arg0!=null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_getStudents:
        {
          data.enforceInterface(descriptor);
          java.util.List<kim.hsl.aidl_demo.Student> _result = this.getStudents();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }

主要分析 TRANSACTION_addStudent 方法 : kim.hsl.aidl_demo.Student 类中 , 实现了序列化与反序列化的方法 , 这里传递参数时 , _arg0 = kim.hsl.aidl_demo.Student.CREATOR.createFromParcel(data) , 调用了 Student 类的反序列化方法 , 将参数先进行反序列化 , 然后赋值给 arg0 , 这样才能得到 Student 类型对象 ;

调用 this.addStudent(_arg0); 代码执行该方法 , 然后通过返回参数 replay , 将结果写回给调用者用户空间进程 ;

代码语言:javascript
复制
        case TRANSACTION_addStudent:
        {
          data.enforceInterface(descriptor);
          kim.hsl.aidl_demo.Student _arg0;
          if ((0!=data.readInt())) {
            _arg0 = kim.hsl.aidl_demo.Student.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          this.addStudent(_arg0);
          reply.writeNoException();
          if ((_arg0!=null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }

6、Stub.Proxy 代理类

Stub 类有一个内部 Proxy 代理类 , 只有当前服务非本地服务 , 即跨进程远程服务时 , 才会使用这个类 ;

代码语言:javascript
复制
private static class Proxy implements kim.hsl.aidl_demo.IMyAidlInterface

在代理类中也需要 AIDL 描述符 , 在 getInterfaceDescriptor 方法中 , 传入 DESCRIPTOR 描述符 ;

代码语言:javascript
复制
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }

代理类内容 :

代码语言:javascript
复制
    private static class Proxy implements kim.hsl.aidl_demo.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;
      }
      /**
           * Demonstrates some basic types that you can use as parameters
           * and return values in AIDL.
           */
      @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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(anInt);
          _data.writeLong(aLong);
          _data.writeInt(((aBoolean)?(1):(0)));
          _data.writeFloat(aFloat);
          _data.writeDouble(aDouble);
          _data.writeString(aString);
          boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      /**
           * in 写入, out 输出, inout 写入和输出
           */
      @Override public void addStudent(kim.hsl.aidl_demo.Student student) throws android.os.RemoteException
      {
      	// 通过 Parcel 池获得两个对象 , 分别用于输入和输出
      	// 输入对象
        android.os.Parcel _data = android.os.Parcel.obtain();
        // 输出对象
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((student!=null)) {
            _data.writeInt(1);
            student.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          // 调用 Binder 的 transact 方法 
          boolean _status = mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().addStudent(student);
            return;
          }
          _reply.readException();
          if ((0!=_reply.readInt())) {
            student.readFromParcel(_reply);
          }
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      /**
           * 获取 Student 集合
           */
      @Override public java.util.List<kim.hsl.aidl_demo.Student> getStudents() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<kim.hsl.aidl_demo.Student> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getStudents, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getStudents();
          }
          _reply.readException();
          _result = _reply.createTypedArrayList(kim.hsl.aidl_demo.Student.CREATOR);
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static kim.hsl.aidl_demo.IMyAidlInterface sDefaultImpl;
    }

Binder 的 transact 方法 ; 进入该方法后 , 会将原来的线程挂起 , 直到返回 , 原来的线程才会继续执行 , 这里非常容易出现 ANR ;

代码语言:javascript
复制
/**
 * 远程对象的基类,由{@link IBinder}定义的轻量级远程过程调用机制的核心部分。
 * 此类是IBinder的一个实现,它提供了此类对象的标准本地实现。
 *
 * <p>大多数开发人员不会直接实现这个类,
 * 而是使用<a href=“{@docRoot}guide/components/aidl.html”>aidl</a>工具来描述所需的接口,
 * 让它生成适当的Binder子类。
 * 然而,您可以直接从Binder派生来实现您自己的定制RPC协议,
 * 或者直接实例化一个原始Binder对象,将其用作可以跨进程共享的令牌。
 *
 * <p>这个类只是一个基本的IPC原语;
 * 它对应用程序的生命周期没有影响,并且只有创建它的进程继续运行时才有效。
 * 要正确使用此功能,您必须在顶级应用程序组件(a{@link android.app.Service}、
 * {@link android.app.Activity}或{@link android.content.ContentProvider})
 * 的上下文中执行此操作,该组件应保持运行。</p>
 *
 * <p>您必须记住流程可能会消失的情况,因此需要稍后重新创建新的活页夹,
 * 并在流程再次启动时重新附加它。
 * 例如,如果您在{@link android.app.Activity}中使用此函数,
 * 则您的活动的进程可能会在活动未启动时被终止;
 * 如果以后重新创建活动,则需要创建新的活页夹,
 * 并再次将其交回正确的位置;
 * 您需要注意的是,您的流程可能由于其他原因(例如接收广播)而启动,
 * 这将不涉及重新创建活动,因此运行其代码以创建新的绑定。</p>
 *
 * @see IBinder
 */
public class Binder implements IBinder {
    /**
     * 默认实现回放地块并调用onTransact。在远程端,transact调用绑定器来执行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;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、分析 AIDL 文件生成的 Java 源文件
    • 1、IMyAidlInterface.java 中的类结构
      • 2、DESCRIPTOR 描述符
        • 3、Stub 构造方法
          • 4、Stub.asInterface 方法
            • 5、Stub.onTransact 方法
              • 6、Stub.Proxy 代理类
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档