【Android】AIDL 总体介绍


概述:

  • AIDL,全称 “Android Interface Defination Language”,中文 “Android 接口定义语言
  • 很多人看到这个词,都不知道怎么理解。首先需要明确的一点是,AIDL一种语言,后面再讲它的作用
  • 我自己第一次接触到 AIDL 是在写 framework/base 下的服务 (Service) 的时候,根据我阅读的相关资料,也要求对 Service 相关知识要有一定了解。所以这里给出两篇 Service 的介绍文章:
  • 研究 AIDL 还需要对 Android 中序列化有所了解,这部分我目前也没有看过相关的资料,以后再补充,下文也会稍微提及

为什么设计这门语言?

  • 目的是为了实现进程间通信
  • 每个进程都有自己的 Dalvik VM 实例,都有自己独立的内存,都在自己狭小的空间过完一生,进程之间并不知道对方的存在。

    看资料说 Android 5.0 系统采用全新的 ART (Android Runtime),抛弃了 Dalvik 虚拟机

  • 但是有时候需要在进程间进行通信,这时候就需要用到 AIDL
  • 通过 AIDL 这门语言,我们可以在一个进程访问另一个进程的数据,甚至调用其中特定的方法

    所谓特定的方法,按我目前的理解,是指我们在 AIDL 中指定的的方法,完全由编写 AIDL 的我们来决定


AIDL 的语法:

  • AIDL 语法非常简单,基本上和 Java 是一样的,只是在一些细微处有差别

    毕竟它只是被创造出来简化 Android 程序员工作的,太复杂不好

  • 文件类型:AIDL文件的后缀是 “.aidl”,而不是 “.java
  • 数据类型:AIDL 默认支持一些数据类型,在使用这些数据类型的时是不需要导包的。除此之外的数据类型,都必须导包,就算目标文件与当前 aidl 文件在同一个包(即同一个目录),都需要导包

    举个例子:
    假设有两个文件:Book.javaBookMark.aidl,都放在 com/kenllf/demo 目录下
    如果需要在 BookMark.aidl 文件中使用 Book 对象,就必须在 BookMark.aidl 文件写上 import com.kenllf.demo;
    这种情况在 java 中是不需要导包的,可以直接使用,这个应该使用过 java 的都知道,不再赘述

  • 默认支持数据类型包括:
    • Java 中的八种基本数据类型,包括 :byte、short(不支持 short,编译不通过,有说时因为 Parcel 无法对 short 进行序列化)、int、long、float、double、boolean、char
    • String 类型
    • CharSequence 类型
    • List 类型 :List 中的所有元素必须是 AIDL 支持的类型之一,或者是一个其它 AIDL 生成的接口,或者是定义的 parcelable(这个下文会有详解)。List 支持泛型
    • Map 类型 :Map 中的所有元素必须是 AIDL 支持的类型之一,或者是一个其它 AIDL 生成的接口,或者是定义的 parcelable。Map 是不支持泛型的
    • Parcelable :所有实现了 Parcelable 接口的对象
    • AILD :所有 AIDL 接口本身也可以在 AIDL 文件中使用
  • 定向 tag :这个极易被忽略——虽然大家都知道,但是很少人会正确地使用它。AIDL 中的定向 tag 表示了在跨进程通信数据的流向
    • in :表示数据只能由客户端 (调用方) 流向服务端 (被调用的服务)
    • out :表示数据只能由服务端 (被调用的服务) 流向客户端 (调用方)
    • inout :则表示数据可在服务端 (被调用的服务) 与客户端 (调用方) 之间双向流通
    • 其中,数据流向是针对在客户端中的那个传入方法对象而言的。
      • in 为定向 tag 的话表现为服务端将会接收到那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动(类似于传值)
      • out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;(类似于传引用)
      • inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动
      • 更详细的解释可参考以下文章:
        你真的理解AIDL中的in,out,inout么?
      • 给一个简单的例子:
      package android.os;
      
      interface ISecurityService {
          int getTime(out byte[] outBuf, out int[] outLen);
          int setTime(in byte[] inBuf, int inLen);
      }
      
      • Java 中的基本类型StringCharSequence 的定向 tag 默认且只能是 in (这里可以反推 byte[] 和 int[] 不属于基本数据类型,其实这个我不是很懂 -_-,只能反推)。还有请注意,请不要滥用定向 tag ,而是要根据需要选取合适的——要是不管三七二十一,全都一上来就用 inout ,等工程大了系统的开销就会大很多——因为排列整理参数的开销是很昂贵的(目前还未理解原理)
  • 两种 AIDL 文件:
    • 一类用来定义 parcelable 对象,以供其它 AIDL 文件使用 AIDL 中非默认支持的数据类型
      • 关于 parcelabel,以后面的代码为例子说一下:

        假设有一个 Book.java 文件,而 IBookManager.aidl 的接口定义需要用到 Book 对象。这时候不能直接导入包,然后直接使用。需要先编写一个 Book.aidl,把 Book 定义为 parcelable 对象,然后在 IBookManager.aidl导入包,再使用 Book 的 parcelable 对象。
        其实比起普通的 java 文件调用 Book 对象,就多了编写 Book.aidl 这一步。
        具体请参考后面的例子。

    • 一类用来定义方法接口,以供系统使用来完成跨进程通信
  • 两类文件都是在 “定义” 些什么,都不涉及具体的实现。这就是为什么它叫做 “Android接口定义语言
  • 举两个具体的使用例子:
// Book.aidl
// 上面所说的第一类 AIDL 文件的例子。
// 这个文件的作用是引入一个序列化对象 Book 供其它的 AIDL 文件使用。
// 看到参考的文章里说,Book.aidl 与 Book.java 的包名应当是一样的,
// 我猜应该是 parcelable 这种用法的情况才是这样,因为我接触过定义 interface 的情况,
// 就有不是同一个包名的,这个有待后续查证。
package android.os;

// 注意哦,parcelable 是小写
parcelable Book;
// IBookManager.aidl
// 第二类 AIDL 文件的例子
package android.os;
// 导入需要使用的非默认支持的数据类型的包(即使是处于同一个包内,看上文解释)
import android.os.Book;

interface IBookManager {

    // 所有的返回值前都不需要加任何东西,不管是什么数据类型
    // 这个有别于 java,java 需要在前面指明是 public 还是 private 还是其它的
    List<Book> getBooks();
    Book getBook();
    int getBookCount();

    // 传参数时除了 Java 基本类型和 String、CharSequence 以外的类型(参考上文 默认支持的数据类型)
    // 都需要在前面加上定向 tag,具体加什么根据需要
    void setBookPrice(in Book book, int price);
    void setBookName(in Book book, String name);
    void addBookIn(in Book book);
    void addBookOut(out Book book);
    void addBookInout(inout Book book);
}

如何使用 AIDL 文件来完成跨进程通信?

  • 在进行跨进程通信时,AIDL 文件中定义的方法包含非默认支持的数据类型与否,会影响具体的定义过程。
  • 如果包含默认支持的数据类型,那么我们只需要编写一个 AIDL 文件
  • 如果包含非默认支持的数据类型,那么通常需要编写 n+1 个 AIDL 文件( n非默认支持的数据类型的种类数

使用数据类实现 Parcelable 接口:

  • 不同进程拥有不同的内存区域,并且只能访问自己的那一块内存区域,所以传一个句柄没有用的 —— 句柄指向一个内存区域,但是目标进程没有访问源进程的内存的权限,传递过去也没用。
  • 既然一个进程无法访问另外一个进程的内存,那么要使数据能够在内存之间流通,就必须将要传输的数据转化为能够在内存之间流通的形式。
    这个过程就叫序列化和反序列化。
  • 假设现在需要将一个对象的数据从客户端传递到服务端,大概的过程是:
    • 首先在客户端对这个对象进行序列化操作,将对象包含的数据转化为序列化流(以便传输),然后将这个序列化流传递到服务端的内存中去
    • 服务端接收到序列化流之后,对序列化流进行反序列化操作,还原其中的数据
    • 通过这种方式就实现了一个进程(服务端)访问另外一个进程(客户端)的数据的目的。
    • 其实这个过程有点像两人之间通过 QQ 传文件夹。直接传文件夹不好传,我先将文件夹压缩(序列化),然后传给你,你接收完之后将压缩包解压为文件夹(反序列化),然后就可以随便使用这个文件夹的内容了。
  • 我们通过 AIDL 进行进程间通信时,选用的序列化方式就是实现 Parcelable 接口

快速生成一个可用的可序列化类的例子:

参考文章:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值