Android~AIDL完成跨进程通信(AIDL文件解析)

一、如何使用AIDL文件来完成跨进程通信?
       AIDL的作用,就是让两个不同的应用间通过Service进行通信(进程通讯IPC),并且远程的Service可以处理多线程。简单来讲就是,两个应用,一个应用对外提供一个远程Service,其他的应用可以并发地访问这个Service,即:C/S模式。
二、AIDL 的编写主要为以下三部分:
       创建一个AIDL文件(扩展名为.aidl);服务端实现该AIDL文件生成的Java接口(系统会自动生成对应的Java接口);暴露一个接口给客户端(通过建立一个Service,在onBind()方法中返回一个Stub类的实例);客户端连接绑定该远程服务。
①创建 AIDL
I、创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化
II、新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
III、Make project ,生成 Binder 的 Java 文件
②服务端
I、创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法
II、在 onBind() 中返回
③客户端
I、实现 ServiceConnection 接口,在其中拿到 AIDL 类
II、bindService()
III、调用 AIDL 类中定义好的操作请求
三、AIDL文件解析
aidl自动生成的Java文件解析
1、文件中有三个类,IMyAidl类、Stub类和Proxy类
(1)ImyAidl类,继承IInterface接口,规范接口方法。
(2)Stub类,静态内部抽象类,继承了Binder,实现了IMyAidl接口的抽象类Stub
①Stub()
       构造方法,内部调用Binder的attachInterface(IInterface owner, String descriptor)方法将Stub当前类(IInterface)和描述写入Binder父类进行绑定
②asInterface(android.os.IBinder obj)
       这个方法是将参数传进来的IBinder转成我们定义的接口类,如果需要的话创建代理类。那么什么是需要的时候通常来说非本应用访问就属于需要的时候。
这个类主要也是实现了注释所说的功能,第一步为空判断,判断传进来的参数是否为空不为空的话继续否则退出,第二步查询接口,通过构造方法中attachInterface传入的说明使用Binder的queryLocalInterface方法查询接口,第三步接口转换,查询接口不为空并且是定义的IMyAidl接口类的话就将查询到的接口强转成IMyAidl接口并返回,第四步启动代理,如果第三步判断没有通过则创建Proxy类启动代理模式。
其实,这个方法是AIDL为我们生成的,如果我们有其他操作,改变这个方法甚至不要这个方法也是没有问题的,具体的就看你的需求了。

 /**
 * 用于将服务端的Binder对象转换成客户端需要的AIDL接口类型的对象区分进程:
 * <p/>
 * 客户端和服务端处于同一进程:直接返回服务端的Stub对象
 * <p/>
 * 客户端和服务端处于不同进程:返回封装好的Stub.proxy对象
 * @param obj
 * @return
 */
public static com.example.aidl.IMyAidl asInterface(android.os.IBinder obj)
    {
    
    
      if ((obj==null)) {
    
    
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.aidl.IMyAidl))) {
    
    
        return ((com.example.aidl.IMyAidl)iin);
      }
      return new com.example.aidl.IMyAidl.Stub.Proxy(obj);
    }

③asBinder()
       这个方法是属于IInterface接口类的,IInterface接口类是使用Binder时必须要继承的类,将实现了IInterface接口的类转成IBinder接口。
④onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException
       此方法默认返回false,如果你想实现自定义的逻辑需要重写这个方法进行事务处理,这个方法的触发需要调用transact()方法。也就是说这个方法是用来处理我们自定义的事务的,我们自定义的事务是什么,在此篇文章中就是IMyAidl接口的方法。
       这个方法在同一进程内通信时不会执行,只有当不同进程间通信时才会执行。

/**
* 该方法运行在服务端,当客户端发起请求,会进入到该方法进行处理
* <p/>
* code:用于标识客户端请求调用的目标方法(即在IOperation中定义的方法标识符)
* <p/>
* data:当请求的目标方法含有参数时,该参数封装了请求的参数
* <p/>
* reply:当请求需要返回结果时,该参数封装了处理结果
*/
@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_getPersonList:
        {
    
    
          // 通过data获取请求的参数
          data.enforceInterface(descriptor);
          java.util.List<com.example.aidl.Person> _result = this.getPersonList();
          // 将结果写入到reply中
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
        case TRANSACTION_addPerson:
        {
    
    
          data.enforceInterface(descriptor);
          com.example.aidl.Person _arg0;
          if ((0!=data.readInt())) {
    
    
            _arg0 = com.example.aidl.Person.CREATOR.createFromParcel(data);
          }
          else {
    
    
            _arg0 = null;
          }
          this.addPerson(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
    
    
          return super.onTransact(code, data, reply, flags);
        }
      }
    }

(3)Proxy类,Stub的静态内部类,实现了IMyAidl接口的具体实现类Proxy
       它是功能代理类,内部持有一个IBinder代理对象,asBinder方法返回的是持有的代理对象。如果是不同进程的话客户端实际使用的接口就是Proxy类,Proxy类将数据进行封装后传给Bidner内核,Binder内核再传递给服务端的Stub类,Stub调用service中实现的方法。
拥有方法
①Proxy(IBinder mRemote)
       构造方法,参数为传入的IBinder对象,持有IBinder对象后,进行操作时需要调用mRemote的transact方法。
②asBinder()
       返回持有的IBinder对象;
③getInterfaceDescriptor()
       返回接口的说明;
④addPerson(Person person)
       IMyAidl接口需要实现的方法,自定义方法;获取两个Parcel实例,data是要传递的方法参数,reply是方法执行完后传回的参数,首先通过writeInterfaceToken将接口描述写入data,之后向data中写入要传递的数据,写入方式依赖Parcel机制,之后执行mRemote.transact()方法,将行为码(TRANSACTION_addPerson)、data、reply、flag一起传入,四个参数的含义和stub的onTransact的参数含义是一致的,最后要记得释放data和reply的资源。

@Override public void addPerson(com.example.aidl.Person person) throws android.os.RemoteException
      {
    
    
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
    
    
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((person!=null)) {
    
    
            _data.writeInt(1);
            person.writeToParcel(_data, 0);
          }
          else {
    
    
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
    
    
            getDefaultImpl().addPerson(person);
            return;
          }
          _reply.readException();
        }
        finally {
    
    
          _reply.recycle();
          _data.recycle();
        }
      }

⑤getPersonList()
       IMyAidl接口需要实现的方法,自定义方法,实现规则和addPerson()是一致的,只是具体实现逻辑上的不同。
此外:
code是行为码:表示要执行的动作,类似Handler.what,IBinder中定义了几个code

PING_TRANSACTION,表示要调用 pingBinder() 方法
DUMP_TRANSACTION,表示要获取 Binder 内部状态
SHELL_COMMAND_TRANSACTION,执行一个 shell 命令
INTERFACE_TRANSACTION,询问被调用方的接口描述符号
TWEET_TRANSACTION
LIKE_TRANSACTION
如果我们需要自定义 code,code 的值范围需要在FIRST_CALL_TRANSACTION(0x00000001)LAST_CALL_TRANSACTION(0x00ffffff) 之间

data是传过来的参数
reply是返回的参数
flag是状态码,表示是否需要阻塞等待返回值,有两个值

0,阻塞
FLAG_ONEWAY(0x00000001),表示 Clienttransact() 是单向调用,执行后立即返回

猜你喜欢

转载自blog.csdn.net/weixin_44495678/article/details/117787008