AIDL

作者: 面向星辰大海的程序员 | 来源:发表于2021-04-02 20:28 被阅读0次

    aidl

    基本使用

    右击main目录选择AIDL,在弹出框输入IxxxInterface,名字随便起,但规范更好,完了就会自动创建一个aidl的目录,期目录与java代码包名一样,然后会生成IxxxInterface.aidl文件,这就是我们要的aidl了,要传递java实体类的得再同目录下创建一个实体类名.aidl,

    如:

    Person.java对应Person.aidl,Person.aidl内容为

    package com.dbf.studyandtest.myaidl;
    
    parcelable Person;
    

    IxxxInterface.aidl内容为,如果是基本数据类型则不需要额外写个实体类的aidl文件,除八大基本数据类型外还支持String,CharSequence、List、Map。

    • 所有自定义类型作为参数都需要加数据走向的方向标记。可以是in、out、inout。
    • 必须用Parcelable 序列化

    八大基本数据类型分为三类:字符型char,布尔型boolean,数值型byte、short、int、long、float、double、而数值类型又分整数类型byte、short、int、long,浮点型float、double,实际上java中还存在一种基本数据类型void

    八种数据类型表示范围如下

    byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。

    short:16位,最大数据存储量是65536,数据范围是-32768~32767之间。

    int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。

    long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。

    float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。

    double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。

    boolean:只有true和false两个取值。

    char:16位,存储Unicode码,用单引号赋值。

    package com.dbf.studyandtest.myaidl;
    import com.dbf.studyandtest.myaidl.Person;
    interface IMyAidlInterface {
    void addPerson(in Person person);//添加Person in out inout  是自定义数据对象的流向,非自定义则不用写默认且只能是in,只能修饰传入的参数
    List<Person> getPersonList();//获取Person集合
    }
    

    in、out、inout 的区别

    AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

    自己理解:在跨进程调用方法中,

    in修饰自定义参数,表示服务端能完整的收到参数数据,并且修改参数数据不会影响客户端

    out修饰自定义参数,表示服务端只能接收到一个参数对象,但是里面的数据是默认值,比如String类型就为null,布尔值就为false,int为0,但是修改这个参数的变量值能同步到客户端传递的对象

    inout则是具备以上两个,值得注意的是只有自定义参数才支持out 和inout,所以说基本数据类型和String等只能单向的流向服务端

    以上的aidl文件写好后,make project一下as自动生成代码

    然后新建一个Service,清单文件注册的时候使用android:process=":aidl"冒号+一个进程名,这样启动的服务在另外一个进程,与这个服务交互达到跨进程通信的目的,稍后说一下为什么跨app之间要声明一样的Service和Aidl(因为要通过全类名找到服务,后面详细说)

    新建一个服务后和普通绑定服务一样,绑定这个服务

    然后Service的onBind方法中得返回一个继承Binder的子类,这里会返回return new IMyAidlInterface.Stub() ,这是As生成的代码

    public static abstract class Stub extends android.os.Binder implements com.dbf.studyandtest.myaidl.IMyAidlInterface{
    ...
    }
    

    可以看出Stub继承了Binder实现了我们定义的aidl,看得出与我们平常绑定服务没什么不一样。

    我们先捋捋平常用的邦定Service

        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //绑定服务必须写的这个Connection,连接成功回调这个方法,在服务Service的onBind方法返回值是IBinder,Binder实现了IBinder,通常是返回Binder的子类实例,所以这里的service就是返回的Binder的子类实例,拿到这个实例对象自然就能调用其方法,与服务Service通信了,然后我们在看看Aidl的实现
                iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                iMyAidlInterface = null;
            }
        };
    

    其实跨进程通信就是通过Binder

    简单看看Stub

     private static final java.lang.String DESCRIPTOR = "com.dbf.studyandtest.myaidl.IMyAidlInterface";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
          this.attachInterface(this, DESCRIPTOR);
        }
        /**
         return new IMyAidlInterface.Stub() {}服务的onBind方法会返回这个
      iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);在我们绑定服务连接成功时会调用这句,
         */
        public static com.dbf.studyandtest.myaidl.IMyAidlInterface asInterface(android.os.IBinder obj)
        {
          if ((obj==null)) {
            return null;
          }
          android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
          if (((iin!=null)&&(iin instanceof com.dbf.studyandtest.myaidl.IMyAidlInterface))) {
            return ((com.dbf.studyandtest.myaidl.IMyAidlInterface)iin);
          }
          return new com.dbf.studyandtest.myaidl.IMyAidlInterface.Stub.Proxy(obj);
        }
    

    Binder

    1617368384(1).jpg

    还有的其他IPC( Internet Process Connection 进程间的通信)通信:

    管道(Pipe)

    信号(Signal)

    跟踪(Trace)

    插口(Socket)

    报文队列(Message)

    共享内存(Share Memory)

    信号量(Semaphore)

    为什么使用Binder:

    性能:共享内存:进程间内存独立但是底层的内存共享,共享底层的内存达到进程间的通信,性能上最优但是操作复杂socket和其他:进程进入底层,底层进入另一个进程需两次拷贝Binder:相对内存操作更简单,底层映射进程,只需拷贝一次到底层即可达到通信

    安全:传统IPC完全依赖上层协议,无法获得对方进程可靠的UID和PID(userID和processID就是用户id和进程id)从而无法鉴别对方身份,Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,使用传统IPC只能由用户在数据包中填入UID和PID,容易被恶意程序利用,其次传统IPC接入点是开放的,无法建立私有通道,比如命名管道的名称,socket的ip地址或文件名都是开放的。Binder基于Client—Server通信模式,为发送方添加UID/PID身份,既支持实名Binder也支持匿名Binder,安全性高*

    省电:传统IPC频繁操作底层

    源码分析:aidl写好后Make project 会在build>generated>source>aidl目录下生成自定义aidl实现代码客户端绑定服务成功后传入一个IBinder,iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);

    private ServiceConnection connection = new ServiceConnection() {
        @Overridepublic
        void onServiceConnected(ComponentName name, IBinder service) {
            Log. * i * (TAG, "onServiceConnected-name=" + name + "===service=" + service);
            iMyAidlInterface = IMyAidlInterface.Stub. * asInterface * (service);
        }
    
        @Overridepublic
        void onServiceDisconnected(ComponentName name) {
            Log. * i * (TAG, "onServiceDisconnected___name==" + name);
            iMyAidlInterface = null;//绑定失败}};
    
        }
    }
    

    在生成的aidl代码中 IMyAidlInterface继承IInterfacepublic interface IMyAidlInterface extends android.os.IInterface {Stup类继承Binder实现IMyAidlInterfacepublic static abstract class Stub extends android.os.Binder implements com.wiite.d2.IMyAidlInterface {调用asInterface方法获得IMyAidlInterface句柄,这时需要传入一个IBinder

      public static com.wiite.d2.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
    //判断是否是本地服务的IBinder,因为传入的是绑定服务成功时候获得的IBinder,
    // 这个IBinder是服务端new IMyAidlInterface.Stub(){}返回的,通过
    // queryLocalInterface()方法的名字就可看出这个方法是判断是否是本地实现IBinder接口
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //如果是绑定本地的服务获得的IBinder,iin就不为null
            if (((iin != null) && (iin instanceof com.wiite.d2.IMyAidlInterface))) {
                //直接返回本地句柄
                return ((com.wiite.d2.IMyAidlInterface) iin);
            }
            //否则返回Proxy 需要传入IBinder(说明Binder核心应该是围绕IBinder这个接口来实现的)从字面上来看这个应该是代理服务端
            return new com.wiite.d2.IMyAidlInterface.Stub.Proxy(obj);
        }
    

    Proxy类主要知识点:

          @Override
            public void addPerson(com.wiite.d2.Person p) throws android.os.RemoteException {//重写Aidl中定义的方法
                android.os.Parcel _data = android.os.Parcel.obtain();//发送包
                android.os.Parcel _reply = android.os.Parcel.obtain();//接收包
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);//Token  应该是验证
                    if ((p != null)) {//创建传入的实体类
                        _data.writeInt(1);//像设置是否可写入
                        p.writeToParcel(_data, 0);//像序列化写入_data
                    } else {
                        _data.writeInt(0);//对应1可能是设置不可写入
                    }
                    //参数1:方法标签,参数2:发送包,参数3:接收包,参数4:传递方向是否可逆。这个方法将会跑到对方进程的onTransact()方法
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                    _reply.readException();//有异常则抛出异常
                } finally {
                    _reply.recycle();//释放资源
                    _data.recycle();//释放资源
                }
            }
    

    onTransact()方法:

        @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_addPerson: {//根据方法标签执行相关代码
                    data.enforceInterface(descriptor);//
                    com.wiite.d2.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.wiite.d2.Person.CREATOR.createFromParcel(data);//将data转为定义的实体类Person对象
                    } else {
                        _arg0 = null;
                    }
                    //调用本地服务实现的addPerson方法将Person实例对象传入 这里就完成了跨进程信息传输了 实际实现是netive实现  看不了
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersonList: {
                    data.enforceInterface(descriptor);
                    java.util.List<com.wiite.d2.Person> _result = this.getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
    

    相关文章

      网友评论

          本文标题:AIDL

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