android开发之Android Interface Definition Language(AIDL)

AIDL(Android Interface Definition Language)IPC(进程间通信)机制是面向对象,轻量级的。通过AIDL定义的接口可以实现服务器端与客户端的IPC通信。在Android上,一个进程不能简单的像访问本进程内存一样访问其他进程的内存。所以,进程间想要对话,需要将对象拆解为操作系统可以理解的基本数据单元,并且有序的通过进程边界。通过代码来实现这个数据传输过程是冗长乏味的,所幸的是Android提供了AIDL工具来帮助为我们完成了此项工作。

注意:仅仅在你需要A应用程序的客户端访问B应用程序的服务器端来实现IPC通信,并且在服务器端需要处理多线程(客户端)访问的情况下使用AIDL。如果不需要使用到进程间的IPC通信,那么通过Binder接口实现将更为合适,如果需要实现进程间的IPC通信,但不需要处理多线程(多客户端),通过Messager接口来实现将更为合适。不管怎样,在使用AIDL之前,应先确保已理解了Bound Service。

AIDL接口的调用采用的是直接的函数调用方式,但你无法预知哪个进程(或线程)将调用该接口。同进程的线程调用和其他进程调用该接口之间是有所区别的:
· 在同进程中调用AIDL接口,AIDL接口代码的执行将在调用该AIDL接口的线程中完成,如果在主UI线程中调用AIDL接口,那么AIDL接口代码的执行将会在这个主UI线程中完成。因此,如果仅仅是本进程中的线程访问该服务,你完全可以控制哪些线程将访问这个服务(但是如果是这样,那就完全没必要使用AIDL了,而采取Binder接口的方式更为合适)。
· 远程进程(其他线程)调用AIDL接口时,将会在AIDL所属的进程的线程池中分派一个线程来执行进程该AIDL代码,所以编写AIDL时,你必须准备好可能有未知线程访问、同一时间可能有多个调用发生(多个线程的访问),所以AIDL接口的实现必须是线程安全的。
· 可以用关键字oneway来标明远程调用的行为属性,如果使用了该关键字,那么远程调用将仅仅是调用所需的数据传输过来并立即返回,而不会等待结果的返回,也即是说不会阻塞远程线程的运行。AIDL接口将最终将获得一个从Binder线程池中产生的调用(和普通的远程调用类似)。如果关键字oneway在本地调用中被使用,将不会对函数调用有任何影响。

定义AIDL接口

AIDL接口使用后缀名位.aidl的文件来定义,.aidl文件使用java语法编写,并且将该.aidl文件保存在 src/目录下(无论是服务端还是客户端都得保存同样的一份拷贝,也就是说只要是需要使用到该AIDL接口的应用程序都得在其src目录下拥有一份.aidl文件的拷贝)。

编译时,Android sdk 工具将会为 src/目录下的.aidl文件在 gen/ 目录下产生一个IBinder接口。服务端必须相应的实现该IBinder接口。客户端可以绑定该服务、调用其中的方法实现IPC通信。

创建一个用AIDL实现的服务端,需要以下几个步骤:
1.创建.aidl文件:
该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口。

2.实现这个接口:
Android SDK将会根据你的.aidl文件产生AIDL接口。生成的接口包含一个名为Stub的抽象内部类,该类声明了所有.aidl中描述的方法,你必须在代码里继承该Stub类并且实现.aidl中定义的方法。

3.向客户端公开服务端的接口:
实现一个Service,并且在onBinder方法中返回第2步中实现的那个Stub类的子类(实现类)。

注意:
服务端AIDL的任何修改都必须的同步到所有的客户端,否则客户端调用服务端得接口可能会导致程序异常(因为此时客户端此时可能会调用到服务端已不再支持的接口)。

1.创建.aidl文件

AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。重要的是必须导入所有非内置类型,哪怕是这些类型是在与接口相同的包中。

默认的AIDL支持一下的数据类型(这些类型不需要通过import导入):

· java语言的原始数据类型(包括 int, long, char, boolen 等等)
· String
· CharSequence:该类是被TextView和其他控件对象使用的字符序列
· List:列表中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。List可以像一般的类(例如List<String>)那样使用,另一边接收的具体类一般是一个ArrayList,这些方法会使用List接口
· Map:Map中的所有元素必须是在此列出的类型,包括其他AIDL生成的接口和可打包类型。一般的maps(例如Map<String,Integer>)不被支持,另一边接收的具体类一般是一个HashMap,这些方法会使用Map接口。

对于其他的类型,在aidl中必须使用import导入,即使该类型和aidl处于同一包内。

定义一个服务端接口时,注意一下几点:

· 方法可以有0个或多个参数,可以使空返回值也可以返回所需的数据。
· 所有非原始数据类型的参数必须指定参数方向(是传入参数,还是传出参数),传入参数使用in关键字标记,传出参数使用out,传入传出参数使用inout。如果没有显示的指定,那么将缺省使用in。
· 在aidl文件中所有的注释都将会包含在生成的IBinder接口中(在Import和pacakge语句之上的注释除外)。
· aidl中只支持成员方法,不支持成员变量。

提示:
限定参数的传输方向非常有必要,因为编组(序列化)参数的代价非常昂贵。

下面是一个.aidl文件的例子

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /** 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);
}

将该.aidl文件保存在工程目录中的 src/目录下,当编译生成apk时,sdk 工具将会在 gen/ 目录下生成一个对应的IBiner接口的.java文件。

如果使用eclipse编写app,那么这个IBinder接口文件将会瞬间生成。如果不是使用eclipse,Ant 工具将会在下次编译你的app时,生成这个IBinder接口文件——所以当你编写.aidl文件之后应该立即使用 ant debug 编译你的app,这样你后续的代码就可以使用这个已生成的IBinder接口类了。

2.实现接口

生成的接口包含一个名为Stub的抽象的内部类,该类声明了所有.aidl中描述的方法。

注意:
Stub还定义了少量的辅助方法,尤其是asInterface(),通过它或以获得IBinder(当applicationContext.bindService()成功调用时传递到客户端的onServiceConnected())并且返回用于调用IPC方法的接口实例,更多细节参见Calling an IPC Method。

要实现自己的接口,就从YourInterface.Stub类继承,然后实现相关的方法(可以创建.aidl文件然后实现stub方法而不用在中间编译,Android编译过程会在.java文件之前处理.aidl文件)。

下面的例子实现了对IRemoteService接口的调用,这里使用了匿名对象。

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值