加入收藏 | 设为首页 | 会员中心 | 我要投稿 厦门网 (https://www.xiamenwang.cn/)- 数据采集、建站、AI开发硬件、专属主机、云硬盘!
当前位置: 首页 > 教程 > 正文

Android进阶之AIDL的使用详情

发布时间:2023-11-06 14:45:06 所属栏目:教程 来源:网络
导读:   AIDL(Android 接口定义语言),可以使用它定义客户端与服务端进程间通信(IPC)的编程接口,在 Android 中,进程之间无法共享内存(用户空间),不同进程之间的通信一般使用 AIDL 来处
  AIDL(Android 接口定义语言),可以使用它定义客户端与服务端进程间通信(IPC)的编程接口,在 Android 中,进程之间无法共享内存(用户空间),不同进程之间的通信一般使用 AIDL 来处理。
 
  主要流程就是在 .aidl 文件中定义 AIDL 接口,并将其添加到应用工程的 src 目录下,创建完成之后 rebuild,Android SDK 工具会自动生成基于该 .aidl 文件的 IBinder 接口,具体的业务对象实现这个接口,这个具体的业务对象也是 IBinder 对象,当绑定服务的时候会根据实际情况返回具体的通信对象(本地还是代理),最后
 
  将客户端绑定到该服务上,之后就可以调用 IBinder 中的方法来进行进程间通信(IPC),下面将从以下几个方面学习 AIDL 的使用:
 
  创建.aildl 文件
 
  具体的业务对象实现基于 .aidl 文件生成的接口
 
  向客户端公开接口
 
  客户端远程调用
 
  验证 AIDL
 
  创建.aildl 文件
 
  在 AIDL 中可以通过可带参数以及返回值的一个或多个方法来声明接口,参数和返回值可以是任意类型,AIDL 中支持的数据类型如下:
 
  java 的 8 种数据类型:byte、short、int、long、float、double、boolean、char
 
  除此之外支持 String、charSequence、List、Map
 
  自定义数据类型
 
  如果业务方法中参数或返回值类型为 List 或 Map 时:
 
  List 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或自己声明的可打包类型。可选择将 List 用作“通用”类(例如,List<String>)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
 
  Map 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)。 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。
 
  当然,AIDL 也支持自定义数据类型,会在下文中介绍。
 
  首先,在工程的 src 目录下创建 .aidl 文件,具体如下图所示:
 
  Android进阶之AIDL的使用详解
 
  然后,在 .aidl 文件中添加具体的业务方法,文件内容如下:
 
  // IPersonAidlInterface.aidl
 
  package com.manu.aidldemo;
 
  // Declare any non-default types here with import statements
 
  interface IPersonAidlInterface {
 
      //具体的业务
 
      void setName(String name);
 
      void setAge(int age);
 
      String getInfo();
 
      /**
 
       * Demonstrates some basic types that you can use as parameters
 
       * and return values in AIDL.
 
       */
 
      void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
 
              double aDouble, String aString);
 
  }
 
  然后,重新 rebuild project , Android SDK 工具会在相应的目录生成对应的与 .aidl 文件同名的 .java 接口文件,具体目录如下:
 
  Android进阶之AIDL的使用详解
 
  具体的业务对象实现基于 .aidl 文件生成的接口
 
  上一步只是使用 AIDL 定义了基本的业务操作,rebuild 之后会生成与 .aidl 相同文件名的 .java 文件,生成的这个接口文件中有一个名称为 Stub 的一个子类,这个子类也是其父接口的抽象实现,主要用于生成 .aidl 文件中的所有方法,Stub 类声明具体如下:
 
  // Stub
 
  public static abstract class Stub extends android.os.Binder implements com.manu.aidldemo.IPersonAidlInterface
 
  显然,Stub 实现了本地接口且继承了 Binder 对象,介于 Binder 对象在系统底层的支持下,Stub 对象就具有了远程传输数据的能力,在生成 Stub 对象的时候会调用 asInterface 方法,具体如下:
 
  // asInterface
 
  public static com.manu.aidldemo.IPersonAidlInterface asInterface(android.os.IBinder obj){
 
      if ((obj==null)) {
 
          return null;
 
      }
 
      // 检索 Binder 对象是否是本地接口的实现
 
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
 
      if (((iin!=null)&&(iin instanceof com.manu.aidldemo.IPersonAidlInterface))) {
 
          return ((com.manu.aidldemo.IPersonAidlInterface)iin);
 
      }
 
      return new com.manu.aidldemo.IPersonAidlInterface.Stub.Proxy(obj);
 
  }
 
  asInterface 这个方法在 Stub 创建的时候会调用,主要功能就是检索 Binder 对象是否是本地接口的实现,根据 queryLocalInterface() 方法返回值判断是否使用代理对象,这个检索过程应该由系统底层支持,如果返回为 null,则创建 Stub 的代理对象,反之使用本地对象来传输数据,下面看一下 Binder 为什么具有远程通信的能力,因为 Stub 继承了 Binder 类,Binder 类具体如下:
 
  // Binder
 
  public class Binder implements IBinder {
 
      //...
 
  }
 
  下面是官网对 IBinder 接口的描述:
 
  远程对象的基础接口,轻量级远程过程调用机制的核心部分,专为执行进程内和跨进程调用时的高性能而设计。该接口描述了与可远程对象交互的抽象协议。不要直接实现这个接口,而是从Binder扩展。
 
  这里我们知道 Binder 实现了 IBinder 接口,也就是说 Binder 具备了远程通信的能力,当不同进程之间(远程)之间通信时,显然使用的是 Stub 的代理对象,这个代理类里面具体的业务处理逻辑,如下面这个方法,具体如下:
 
 
  //具体的业务
 
  @Override
 
  public void setName(java.lang.String name) 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.writeString(name);
 
          // 这个方法会最终调用 onTransact 方法
 
          mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
 
          _reply.readException();
 
      }
 
      finally {
 
          _reply.recycle();
 
          _data.recycle();
 
      }
 
  }
 
  这里主要就是将数据序列化,然后在系统跨进程支持下最终调用 onTransact() 方法,下面是 onTransact() 方法,具体如下:
 
  @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_setName:
 
          {
 
              //...
 
              // 最终调用了 Stub 里面的业务方法
 
              this.setName(_arg0);
 
              //...
 
          }
 
      }
 
  }
 
  显然,这个方法在当系统回调给开发者的时候,传递回来的 code 是一个常量,在跨进程时,每个具体的服务(方法)都会对应一个编号,然后根据这个编号来执行相应的服务(业务),这里说到了最后要执行的具体业务,那么这个业务要体现在什么地方呢,从上面可知 Stub 是一个抽象类,那么它所提供的具体业务必然需要一个具体的实现类来完成,下面实现这个具体的业务类,具体如下:
 
  /**
 
   * Created by jzman
 
   * Powered by 2018/3/8 0008.
 
   */
 
  public class IPersonImpl extends IPersonAidlInterface.Stub {
 
      private String name;
 
      private int age;
 
      @Override
 
      public void setName(String name) throws RemoteException {
 
          this.name = name;
 
      }
 
      @Override
 
      public void setAge(int age) throws RemoteException {
 
          this.age = age;
 
      }
 
      @Override
 
      public String getInfo() throws RemoteException {
 
          return "My name is "+name+", age is "+age+"!";
 
      }
 
      @Override
 
      public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
 
      }
 
  }
 
  这个类就是对外提供的具体业务类,同时其实例也是一个 Binder 对象。
 
  向客户端公开接口
 
  创建一个 Service 以便对外提供具体的业务,具体如下:
 
  // Service
 
  public class PersonService extends Service {
 
      public PersonService() {
 
      }
 
      @Override
 
      public IBinder onBind(Intent intent) {
 
          return new IPersonImpl();
 
      }
 
  }
 
  当外部调用 bindService() 方法绑定服务时,就会调用 onBind() 方法返回 IBinder 对象,这个 IBinder 对象也是具体的业务对象,如这里的 onBind() 方法返回的也是具体的业务对象,两者是统一的。此外,创建的 Service 要在 AndroidManifest.xml 文件中声明,具体如下:
 
  <service
 
      android:name=".PersonService"
 
      android:enabled="true"
 
      android:exported="true"
 
      android:process=":remote">
 
  </service>
 
  其中使用 process 关键字表示为该服务开启一个独立的进程,remote 可任意,表示进程名称,":"将会在主进程(进程名为包名)添加新名称作为新进程的名称,如 com.manu.study 将会变成 com.manu.study:remote。
 
  客户端远程调用
 
  通过上面几步完成了服务的搭建,并将服务运行在独立进程中,下面主要就是客户端的具体调用了,具体实现参考如下:
 
  // Client
 
  public class MainActivity extends AppCompatActivity {
 
      private static final String TAG = "MainActivity";
 
      private IPersonAidlInterface iPersonAidlInterface;
 
      @Override
 
      protected void onCreate(Bundle savedInstanceState) {
 
          super.onCreate(savedInstanceState);
 
          setContentView(R.layout.activity_main);
 
      }
 
      public void bindServiceClick(View view) {
 
          Log.i(TAG,"绑定服务...");
 
          Intent intent = new Intent(this,PersonService.class);
 
          // 绑定服务时自动创建服务
 
          bindService(intent,conn, Context.BIND_AUTO_CREATE);
 
      }
 
      public void unbindServiceClick(View view) {
 
          Log.i(TAG,"解绑服务...");
 
          unbindService(conn);
 
      }
 
      public void callRemoteClick(View view) {
 
          Log.i(TAG,"远程调用具体服务...");
 
          try {
 
              iPersonAidlInterface.setName("Tom");
 
              iPersonAidlInterface.setAge(10);
 
              String info = iPersonAidlInterface.getInfo();
 
              System.out.println("这是远程调用的服务信息:"+info);
 
          } catch (RemoteException e) {
 
              e.printStackTrace();
 
          }
 
      }
 
      private ServiceConnection conn = new ServiceConnection() {
 
          @Override
 
          public void onServiceConnected(ComponentName name, IBinder service) {
 
              // 根据实际情况返回 IBinder 的本地对象或其代理对象
 
              iPersonAidlInterface = IPersonAidlInterface.Stub.asInterface(service);
 
              System.out.println("具体的业务对象:"+iPersonAidlInterface);
 
          }
 
          @Override
 
          public void onServiceDisconnected(ComponentName name) {
 
              // Service 意外中断时调用
 
          }
 
      };
 
  }
 
  上述代码是客户端调用具体服务的过程。
 

(编辑:厦门网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章