Translated from google
AIDL(Android Interface Definition Language)安卓接口定义语言,为了IPC-即进程间通信。 既然是两个APP进程间的通信,如何让对方程序理解你的话,将对象分解成操作系统能够理解的原始数据,然后操作系统处理对象信号编辑后发送给对方。这种信 号的编辑比较繁琐,顾Android 引入了AIDL。
注意:AIDL只在进程间通信的时候有必要这么做,如果是同个进程间的,可以用绑定形式的Service,如果你还想IPC,但不是多线程访问的,你可以使用Messenger。
设计AIDL时请注意(调用方法):
1.当你在本地进程直接调用方法的时候,实际上该方法所处的线程与本地是同一个,当你另外开个线程调用方法的时候,这才是真正在服务端执行的代码,也就是不同进程。
这句话的言外之意就是,如果只是本地这么调用的话,你应该设计个Bound类型的Service或者其它,而不是AIDL。
2.注意AIDL支持多线程访问,问题就来了,你要确保方法的同步,以避免一些问题。
3.oneway 关键字会修改远程调用方法的行为。当你使用的时候,远程方法调用不会阻塞。 反之则不然。 当然如果你在本地调用的,这个木有影响,还是同步的。
定义AIDL接口
1.创建.aidl文件
后缀名必须是.aidl,然后里面定义的方式类似于java接口的定义方式。
AIDL支持下列数据类型:
默认的:所有原始数据类型:比如int、long之类的。String、CharSequence、List、Map。关于list和Map你可能使用的是多态,但是最终对方收到的仍然是ArrayList或者HashMap.
List或者Map中的泛型参数也必须是所支持的参数,比如Map<String,Integer>是不支持的。
如果你要使用其他的数据类型,你需要Import,注意即便是在同个包中的,也是需要Import。
你需要注意的:
1.方法可以0或者多个参数,返回一个或者void。
2.所有非原始数据类型的需要一个直接的标签,来指示数据出去的方式。in、out或者inout。
原始类型默认是in,也只能是in。
3.所有在.aidl的代码注释都被包含在IBinder接口。(除了导入包名之前的语句)
4.AIDL中你只能定义方法,不像java还可以搞些静态变量。
下面列出DEMO:
// 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/目录下,当编译你的工程时,SDK工具会自动产生IBinder接口文件在你的gen/目录下。
产生的文件名字与.aidl的名字一致,不过扩展名变成了.java的。
2.实现接口
刚才说到的产生的那个java文件,包含一个子类,名字为Stub,是其父接口的抽象实现,比如YourInterface.Stub,它会声明所有.aidl文件中的方法。
你需要去实现这个抽象类比如:
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
关于AIDL的实现仍然有几点要说的:
别人要调用你,注意别ANR,别把事情都放在主线程做了。
你的异常若不抛出,则会被送往调用者。
3.向客户端暴露接口
当然就设计Service了,look:
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}
客户端这边绑定服务,使用bindService这种方式,当然就需要ServiceConnection了,在绑定的时候把mBinder弄过来,然后就可以操作了。
IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = IRemoteService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
}
};
当然你会问,客户端这边又没有定义方法? 答:需要copy一份aidl到客户端处,gen下面也会同时自动生成。