美文网首页
AIDL跨进程通信

AIDL跨进程通信

作者: Mr_Fly | 来源:发表于2022-02-26 14:33 被阅读0次

    前言

    • 此教程的目的是教会大家如何使用AIDL,包括定义AIDL服务、调用AIDL服务、传递复杂对象、AIDL回调客户端等。

    概述

    • 全称Android Interface Definition Language。

    • 像其他IDLs一样,允许你定义编程接口,以便客户端和服务能通过内部进程通信(interprocess communication,IPC)。

    定义AIDL服务

    1. 创建.aidl文件
    2. SDK生成对应.java文件和Stub内部类
    3. 通过Service子类将接口暴露给外界

    1. 创建.aidl文件

    • 用Java编程语言来构造.aidl文件。每个.aidl文件必须定义一个带方法声明的接口。

    • AIDL支持以下数据类型:

      1. Java基本类型,即int、long、char等;
      2. String;
      3. CharSequence;
      4. List
        • List中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
      5. Map
        • Map中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
      6. 其他类型,必须要有import语句,即使它跟.aidl是同一个包下。
    • AIDL中的方法和变量

      • 方法可有零、一或多个参数,可有返回值或void。
      • 所有非基本类型的参数都需要标签来表明这个数据的去向:
        1. in,表示此变量由客户端设置;
        2. out,表示此变量由服务端设置;
        3. inout,表示此变量可由客户端和服务端设置;
        4. 基本类型只能是in
      • 只expose方法,不会expose静态变量
    • .aidl文件保存在项目/src/<SourceSet>/aidl目录下。

      // IRemoteService.aidl
      package com.daking.aidl;
      
      // Declare any non-default types here with import statements
      
      interface IRemoteService {
       /**
          * 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);
      }
      
      

    2. SDK生成对应.java文件和Stub内部类

    • 当编译APP时,SDK工具会将项目/src/<SourceSet>/aidl目录下的.aidl文件一个个在项目/build/generated/source/aidl目录下生成IBinder接口.java文件。两个文件名一样,只是后缀不同。如IRemoteService.aidl生成IRemoteService.java

    • Stub内部类

      • .aidl文件编译后生成的.java文件中自动生成的内部类。
      • public static abstract声明。
      • extends android.os.Binder。
      • 实现.aidl文件中定义的接口,且声明其所有方法。
    • 实现Stub内部类要注意

      • 对于传过来的调用,无法保证是在主线程中执行的。Service必须要考虑多线程和线程安全。
      • 默认情况下,RPC都是异步的。避免在主线程中调用AIDL,不然可能会导致ANR。
      • 不能给调用方回抛异常

    3. 通过Service子类将接口暴露给外界

    • 需要创建Service子类,并在onBind()中返回Stub内部类。

      package com.daking.aidl;
      
      import android.app.Service;
      import android.content.Intent;
      import android.os.IBinder;
      import android.os.RemoteException;
      
      public class RemoteService extends Service {
          public RemoteService() {}
      
          @Override 
          public IBinder onBind(Intent intent) {
              return mBinder;
          }
      
          private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
              @Override
              public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
      
              }
          };
      }
      
      
    • <service>配置,设置exported为true、自定义action名等。

      <service
          android:name=".RemoteService"
          android:enabled="true"
          android:exported="true">
          <intent-filter>
              <action android:name="com.daking.aidl.RemoteService" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </service>
      
      

    调用AIDL服务

    • 若客户端组件和服务分开在不同APP,那么客户端所在APP的/src/<SourceSet>/aidl目录下必须要有一份.aidl副本。

    • 绑定AIDL服务

      Intent intent = new Intent();
      intent.setPackage("com.daking.aidl");
      intent.setAction("com.daking.aidl.RemoteService");
      bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
      
      
    • 当客户端onServiceConnected()接收到这个AIDL服务返回的IBinder时,必须要将其强制类型转换为YourServiceInterface类型。如

      public void onServiceConnected(ComponentName className, IBinder service) {
          mIRemoteService = IRemoteService.Stub.asInterface(service);
      } 
      
      
    • 可像普通对象一样调用mIRemoteService。注意,AIDL服务默认是运行在主线程中,若里面有耗时操作,应该在子线程中调用AIDL。

    传递复杂对象

    • 在AIDL中传递的复杂对象必须要实现Parcelable接口,这是因为Parcelable允许Android系统将复杂对象分解成基本类型以便在进程间传输。

    • Parcelable实现类

      1. implements Parcelable;
      2. 实现writeToParcel(),它会读取这个对象的当前状态并写入一个包中;
      3. 实现describeContents();
      4. 添加一个实现Parcelable.Creator接口的静态变量CREATOR。
      package com.daking.aidl;
      
      import android.os.Parcel;
      import android.os.Parcelable;
      
      public class RequestVO implements Parcelable {
      
          private String name;
          private String type;
      
          public RequestVO() {}
      
          public RequestVO(Parcel source) {
              super();
              this.setName(source.readString());
              this.setType(source.readString());
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getType() {
              return type;
          }
      
          public void setType(String type) {
              this.type = type;
          }
      
          public int describeContents() {
              return 0;
          }
      
          public void writeToParcel(Parcel dest, int flags) {
              dest.writeString(name);
              dest.writeString(type);
          }
      
          public static final Parcelable.Creator<RequestVO> CREATOR = new Parcelable.Creator<RequestVO>() {
              public RequestVO createFromParcel(Parcel source) {
                  return new RequestVO(source);
              }
      
              public RequestVO[] newArray(int size) {
                  return new RequestVO[size];
              }
          };
      }
      
      
    • 若客户端组件和服务分开在不同APP,必须要把该Parcelable实现类.java文件拷贝到客户端所在的APP,包路径要一致

    • 另外,需要为这个Parcelable实现类定义一个相应的.aidl文件。与AIDL服务接口.aidl同理,客户端所在APP的/src/<SourceSet>/aidl目录下也要有这份副本。

      package com.daking.aidl;
      
      parcelable RequestVO;
      
      
    • 将复杂对象作为AIDL接口的形参时,记得加上in

      import com.daking.aidl.RequestVO;
      
      interface IRemoteService {
          void request(in RequestVO vo);
      }
      
      

    AIDL服务回调客户端

    1. 自定义回调接口.aidl。

      package com.daking.aidl;
      
      import com.daking.aidl.ResponseVO; // 自定义结构类,具体实现可参考上一节。
      
      interface ICallback {
          void onResult(in ResponseVO vo);
      }
      
      
    2. AIDL服务.aidl提供接口给客户端注册和注销此回调。

      package com.daking.aidl;
      
      import com.daking.aidl.RequestVO;
      import com.daking.aidl.ICallback;
      
      interface IRemoteService {
          void request(in RequestVO vo);
          void registerCallback(in ICallback cb);
          void unregisterCallback(in ICallback cb);
      }
      
      
    3. AIDL服务.java的具体实现。

      public class RemoteService extends Service {
          // ICallback列表
          private RemoteCallbackList<ICallback> icallbacks;
      
          @Override
          public IBinder onBind(Intent intent) {
              icallbacks = new RemoteCallback<ICallback>();
              return mBinder;
          }
      
          private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
              @Override
              public void request(in RequestVO vo) {
                  sendResponse();
              }
              @Override
              public void registerCallback(in ICallback cb) {
                  if(cb != null) {
                      icallbacks.register(cb);
                  }
              }
              @Override
              public void unregisterCallback(in ICallback cb) {
                  if(cb != null) {
                      icallbacks.unregister(cb);
                  }
              }
          };
      
          private void sendResponse() {
              ResponseVO vo = new ResponseVO();
      
              // 以广播的方式进行客户端回调
              int len = icallbacks.beginBroadcast();
              for (int i = 0; i < len; i++) {
                  try {
                      icallbacks.getBroadcastItem(i).onResult(vo);
                  } catch (RemoteException e) {
                      e.printStackTrace();
                  }
              }
              // 记得要关闭广播
              icallbacks.finishBroadcast();
          }
      }
      
      
    4. 客户端创建回调接口的实现对象,并注册到AIDL。

      protected ICallback callback = new ICallback.Stub() {
          @Override
          public void onResult(ResponseVO vo) {
              // AIDL回调客户端后的业务处理
          }
      };
      
      // mService为AIDL服务
      mService.registerCallback(callback);
      
    5. Gradle跨module引用aidl

    工程中有两个module,module A和module B,module A在build.gradle中通过compile project(:B)引用了module B。module B定义了com.luo.TestB.aidl,同时module A定义的TestA.aidl并且Test B.aidl,但是as编译失败,提示无法找到TestB.aidl。原因是gradle编译module B时默认不会将aidl文件打进aar中。

    解决方法
    在module B的build.gradle中添加aidlPackageWhiteList "com/luo/TestB.aidl",即如下所示

    apply plugin: 'com.android.library'

    dependencies {
    ...
    }

    android {
    ...
    aidlPackageWhiteList "com/luo/TestB.aidl"
    ...
    }

    相关文章

      网友评论

          本文标题:AIDL跨进程通信

          本文链接:https://www.haomeiwen.com/subject/rkmqrrtx.html