1. 概述
AIDL:Android Interface Definition Language,即Android接口定义语言。在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。(摘自Google developer网站)。
只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。
2. AIDL 支持的数据类型
a. Java 编程语言中的所有基本数据类型。(如 int、long、char、boolean 等等)
b. String 和 CharSequence
c. List 和Map:List /Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。
d. AIDL 生成的接口。
e. 实现Parcelable接口的类
3. AIDL 实现步骤:
服务端:
1. 创建 .aidl 文件
此文件定义带有方法签名的编程接口。
2. 实现接口
Android SDK 工具会生成一个以 .aidl
文件命名的 .java
接口文件。生成的接口包括一个名为 Stub
的子类,这个子类是其父接口(例如,YourInterface.Stub
)的抽象实现,用于声明 .aidl
文件中的所有方法。
3. 向客户端公开接口
实现 Service 并重写onBind() 以返回 Stub 类的实现。
客户端:
1.在项目中加入 .aidl 文件
2. 实现 ServiceConnection 。
3. 调用 bindService ,以传入 ServiceConnetion。
4. 在 onServiceConnected() 实现中,将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。
4. AIDL实例
以下为具体的代码实现:
服务端:
1. 创建要传递的实体类 Person,并实现 Parcelable 接口。
package cn.zzw.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class PersonInfo implements Parcelable{
private String name;
private int age;
private String sex;
private float height;
public PersonInfo(String name, int age, String sex, float height) {
this.name = name;
this.age = age;
this.sex = sex;
this.height = height;
}
public PersonInfo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public static Creator<PersonInfo> getCREATOR() {
return CREATOR;
}
protected PersonInfo(Parcel in) {
name = in.readString();
age = in.readInt();
sex = in.readString();
height = in.readFloat();
}
public static final Creator<PersonInfo> CREATOR = new Creator<PersonInfo>() {
@Override
public PersonInfo createFromParcel(Parcel in) {
return new PersonInfo(in);
}
@Override
public PersonInfo[] newArray(int size) {
return new PersonInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeString(sex);
dest.writeFloat(height);
}
}
注意:此文件所在的包名要和 .aidl 文件所在的包名一样。
2. 创建 IPerson.aidl 文件
// IPerson.aidl
package cn.zzw.aidl;
import cn.zzw.aidl.PersonInfo;
// Declare any non-default types here with import statements
interface IPerson {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String sayHello(in PersonInfo personInfo);
int sumnNum(int num1,int num2);
}
注意: 需要import 相关的实体类,否则会报如下错误:
Process 'command 'D:\Android\sdk\build-tools\28.0.3\aidl.exe'' finished with non-zero exit value 1
3. 对于实体类 Person 也必须创建对应的 .aidl 文件
// PersonInfo.aidl
package cn.zzw.aidl;
parcelable PersonInfo;
以上三步创建后,目录如下:
4. 创建隐式意图的Service(对于此文件所在的包没有要求),并在onBind方法返回已经实现AIdl 接口的对象。
package cn.zzw.aidlserverdemo.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import cn.zzw.aidl.IPerson;
import cn.zzw.aidl.PersonInfo;
public class AidlServerService extends Service {
private class MyBinder extends IPerson.Stub{
@Override
public String sayHello(PersonInfo personInfo) throws RemoteException {
if(null!=personInfo)
{
String name = personInfo.getName();
int age = personInfo.getAge();
float height = personInfo.getHeight();
String sex = personInfo.getSex();
return "Hello,My Name is "+name;
}
return null;
}
@Override
public int sumnNum(int num1, int num2) throws RemoteException {
return num1+num2;
}
};
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
}
在 AndroidManifest.xml 中注册隐式意图的Service:
<service android:name=".service.AidlServerService">
<intent-filter>
<action android:name="cn.zzw.PERSON.SERVER"></action>
</intent-filter>
</service>
至此Server端的实现就算完成了,接下来就是客户端如何调用了。
客户端:
1. 在客户端中实现服务端中的第 1,2,3 步。
注意:文件所在的包名一定要和服务端一样!!!
2. 绑定远程服务,获取服务对象。
private void bindToClientService() {
intent = new Intent();
intent.setAction("cn.zzw.PERSON.SERVER");
intent.setPackage("cn.zzw.aidlserverdemo");
bindService(intent,conn,BIND_AUTO_CREATE);
}
ServiceConnection conn =new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService= IPerson.Stub.asInterface(service);
Log.d(TAG,"Service Connect Successfully");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG,"Service Connect Fail");
}
};
3. 调用远程服务中的方法
public void onClick(View v) {
switch (v.getId()) {
case R.id.mBtn_hello:
if(mService!=null)
{
String msgInfo = null;
try {
msgInfo = mService.sayHello(mInfo);
Toast.makeText(this,msgInfo,Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}else {
Log.d(TAG,"Service is Null");
bindToClientService();
}
break;
case R.id.mBtn_calculate:
if(mService!=null)
{
String msgInfo = null;
try {
int sumInfo = mService.sumnNum(50,50);
Toast.makeText(this,""+sumInfo,Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}else {
Log.d(TAG,"Service is Null");
bindToClientService();
}
break;
}
}
效果:
先尝试只安装Client,会发现绑定不了Service:
log如下:
D/Client: Service is Null
D/Client: Service is Null
安装服务端后,再次运行客户端后:
附上以上实例的代码:
https://download.csdn.net/download/zzw0221/11248522
5. AIDL 的工作原理
当创建AIDL文件并Clean Project 代码后,会生成相应的Java文件:
先来一段伪代码:类整体结构
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\AndroidProject\\AIDLServerDemo\\app\\src\\main\\aidl\\cn\\zzw\\aidl\\IPerson.aidl
*/
package cn.zzw.aidl;
// Declare any non-default types here with import statements
public interface IPerson extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements cn.zzw.aidl.IPerson //1
{
...
}
public java.lang.String sayHello(cn.zzw.aidl.PersonInfo personInfo) throws android.os.RemoteException; //2
public int sumnNum(int num1, int num2) throws android.os.RemoteException; //3
}
注释2 和注释3 就是我们在.aidl 中定义的方法。
注释1 是一个静态的抽象类,并且继承了Binder类。Binder是Android中实现IPC方式。AIDL就是利用Binder来实现的。
这里就不对于Binder进行解释,后面会对Binder的源码进行解读,再记录一篇关于Binder的原理。
看看Stub这个类:
public static abstract class Stub extends android.os.Binder implements cn.zzw.aidl.IPerson {
private static final java.lang.String DESCRIPTOR = "cn.zzw.aidl.IPerson";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an cn.zzw.aidl.IPerson interface,
* generating a proxy if needed.
*/
public static cn.zzw.aidl.IPerson asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof cn.zzw.aidl.IPerson))) {
return ((cn.zzw.aidl.IPerson) iin);
}
return new cn.zzw.aidl.IPerson.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@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_sayHello: {
data.enforceInterface(descriptor);
cn.zzw.aidl.PersonInfo _arg0;
if ((0 != data.readInt())) {
_arg0 = cn.zzw.aidl.PersonInfo.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.lang.String _result = this.sayHello(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_sumnNum: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sumnNum(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements cn.zzw.aidl.IPerson {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String sayHello(cn.zzw.aidl.PersonInfo personInfo) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((personInfo != null)) {
_data.writeInt(1);
personInfo.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int sumnNum(int num1, int num2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_sumnNum, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sumnNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
Stub类的类结构如下:
下面两个int常量是用来标识我们在接口中定义的方法的:
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sumnNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
DESCRIPTOR 常量是Binder的唯一标识。
private static final java.lang.String DESCRIPTOR = "cn.zzw.aidl.IPerson";
asInterface 方法非常熟悉,上面客户端才用到的,用于将服务端的Binder对象转换为客户端所需要的接口对象。该过程区分进程,如果进程一样,就返回服务端Stub对象本身,否则就返回封装后的Stub.Proxy对象。
public static cn.zzw.aidl.IPerson asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof cn.zzw.aidl.IPerson))) {
return ((cn.zzw.aidl.IPerson) iin);
}
return new cn.zzw.aidl.IPerson.Stub.Proxy(obj);
}
onTransact 方法是实现 Binder 类后重写的方法。这是运行在服务端的Binder线程中的,当客户端发起远程请求后,在底层的方法就会触发此方法。
5. 总结
AIDL的原理就是Binder,AIDL的最终结果就是让进程间通讯变得简单,自动的完成了参数序列化发送以及解析返回数据的一系列麻烦。