AIDL是什么,作用是什么
项目中涉及到两个应用即两个进程间进行数据通信,而Android中进程之间的内存地址是相互独立的,一个进程无法访问另一个进程的内存地址。这里简单介绍下进程的概念,进程是一个程序或者应用的实体,每个进程都拥有自己独立的内存地址空间。官方文档上讲,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供您操作的对象。编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL来完成跨进程通信。
Android中的跨进程其实就是IPC(Inter-Process Communication)通信,而AIDL正是IPC通信的机制。
AIDL全称是Android Interface Definition Language,是安卓接口定义的意思,通过定义相关的接口来实现跨进程通信。
AIDL使用
1.AIDl支持的数据类型
AIDL 使用一种简单语法,允许您通过一个或多个方法(可接收参数和返回值)来声明接 口。参数和返回值可为任意类型,甚至是 AIDL 生成的其他接口。
您必须使用 Java 编程语言构建 .aidl 文件。每个 .aidl 文件均须定义单个接口,并且只需要接口声明和方法签名。
默认情况下,AIDL 支持下列数据类型:
Java 编程语言中的所有原语类型(如 int、long、char、boolean 等java基本数据类型)
1.String
2.CharSequence
3.List
List 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。您可选择将 List 用作“泛型”类(例如,List)。尽管生成的方法旨在使用 List 接口,但另一方实际接收的具体类始终是 ArrayList。
4. Map
Map 中的所有元素必须是以上列表中支持的数据类型,或者您所声明的由 AIDL 生成的其他接口或 Parcelable 类型。不支持泛型 Map(如 Map<String,Integer> 形式的 Map)。尽管生成的方法旨在使用 Map 接口,但另一方实际接收的具体类始终是 HashMap。
5.其他 AIDL 生成的接口
6.实现 Parcelable 的类数据类型
2.编写AIDL相关代码
编写服务端代码
创建实体类Person类,实现Parcelable接口,用于跨进程通信的实体类。
public class People implements Parcelable {
private String id;
private String name;
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<People> CREATOR = new Creator<People>() {
@Override
public People createFromParcel(Parcel in) {
People people = new People();
people.setId(in.readString());
people.setName(in.readString());
return people;
}
@Override
public People[] newArray(int size) {
return new People[size];
}
};
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建.aldl文件,这里包括两个文件,一个是实体类People的文件,一个是接口文件
// People.aidl
package com.uniubi.aidlapplication;
parcelable People;
// People.aidl
package com.uniubi.aidlapplication;
import com.uniubi.aidlapplication.People;
interface IRemoteService {
void basicTypes(int a, long b, in People person, in String d);
}
这里要记住需要导入People类,否则.aidl文件会找不到。
实现接口
点击android studio的make project
会在build/generated//Users/chenxuming/generated/aidl_source_output_dir/debug/compileDebugAidl/out/目录下生成IRemoteService.java文件。
记住一定要在当前moudle的gradle文件里指定.aidl文件路径,否则编译会找不到People这个类,
在这里吃了个大亏。
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
查看生成的java文件,可以看出接口IRemoteService类继承android.os.IInterface接口,里面包含了一个继承Binder的Sub子类,并且这个Sub子类实现IRemoteService接口,后面我们将会具体讲讲这个生成的IRemoteService类具体源码。
创建Service,并在其中创建binder对象,通过onBind方法返回这个binder对象,到此为止服务端代码编写完成。
public class RemoteService extends Service {
private static final String TAG = "RemoteService";
private IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override
public void basicTypes(int a, long b, People person, String d) throws RemoteException {
Log.i(TAG, "basicTypes" + person.getName());
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
向客户端公开接口
1.将服务端src/main下面的整个aidl目录下的文件拷贝到客户端相同的位置,在客户端项目下同样别忘记了在当前module下的gradle配置sourceSets,能让.aidl文件找到People类,然后make project。
2.绑定远程服务,这里必须是绑定远程服务,“com.uniubi.aidlapplication.RemoteService”是远程服务的actiob name,通过隐式的方式绑定。
Intent intent1 = new Intent();
intent1.setComponent(new ComponentName("com.uniubi.aidlapplication", "com.uniubi.aidlapplication.RemoteService"));
bindService(intent1, serviceConnection, BIND_AUTO_CREATE);
private IRemoteService iRemoteService;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("MainActivity", "onServiceConnected");
iRemoteService = IRemoteService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("MainActivity", "onServiceDisconnected");
}
};
通过代码可以看出绑定远程服务成功后,能拿到IBinder对象,然后通过IRemoteService.Stub.asInterface(service)转成我们需要的iRemoteService对象,然后通过
iRemoteService对象,就可以进行跨进程请求了。
try {
if(iRemoteService != null){
iRemoteService.basicTypes(1, 2, people, "呵呵");
}
} catch (RemoteException e) {
Log.i("MainActivity", e.getMessage());
e.printStackTrace();
}
AIDL源码分析
生成的代码格式比较乱,最好先格式化下方便阅读。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/chenxuming/AndroidStudioProjects/AIDLClient/app/src/main/aidl/com/uniubi/aidlapplication/IRemoteService.aidl
*/
package com.uniubi.aidlapplication;
public interface IRemoteService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.uniubi.aidlapplication.IRemoteService {
private static final java.lang.String DESCRIPTOR = "com.uniubi.aidlapplication.IRemoteService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.uniubi.aidlapplication.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.uniubi.aidlapplication.IRemoteService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.uniubi.aidlapplication.IRemoteService))) {
return ((com.uniubi.aidlapplication.IRemoteService) iin);
}
return new com.uniubi.aidlapplication.IRemoteService.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_basicTypes: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
com.uniubi.aidlapplication.People _arg2;
if ((0 != data.readInt())) {
_arg2 = com.uniubi.aidlapplication.People.CREATOR.createFromParcel(data);
} else {
_arg2 = null;
}
java.lang.String _arg3;
_arg3 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.uniubi.aidlapplication.IRemoteService {
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 void basicTypes(int a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeLong(b);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
_data.writeString(d);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void basicTypes(int a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) throws android.os.RemoteException;
}
不难发现IRemoteService这个类由两部分组成,一部分是声明的接口,一部分是继承Binder类的Sub内部类,这个Sub内部类有两个主要的方法,asInterface() 和onTransact()方法,还有个内部类Proxy,一看名字就知道是代理模式,这里就不细说代理模式了。
一步步来,首先看下iRemoteService = IRemoteService.Stub.asInterface(service)这行代码,将服务端的binder对象解析成客户端的iRemoteService对象,该方法返回的是IRemoteService.Stub.Proxy对象,也就是说iRemoteService对象就是IRemoteService.Stub.Proxy,然后再看调用 iRemoteService.basicTypes(1, 2, people, “呵呵”)的地方,其实看的就是Proxy对象的basicTypes方法,看到关键代码mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);是通过mRemote对象的transact方法来进行通信,mRemote对象就是远程服务端的Sub对象,点进去查看源码,主要查看onTransact方法,而上面也讲到Sub类里是实现了Binder类的onTransact方法的(上面已贴出来,这里不重复贴出)。
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
发现onTransact是通过code来指定具体调用行为,关键代码this.basicTypes(_arg0, _arg1, _arg2, _arg3);而这里的this就是远程服务端的Sub对象,具体basicTypes方法实现得去服务端看,客户端是看不到。到了这里整个客户端服务端跨进程通信的流程讲解完毕。
总结
1.asInterface(android.os.IBinder obj)
用于服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的(如果客户端和服务端位于同一个进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象)
2.onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
客户端跨进程请求主要是通过远程服务binder对象的onTransact方法完成。
code : 确定客户端请求的目标方法是什么。
data : 如果目标方法有参数的话,就从data取出目标方法所需的参数。
reply : 当目标方法执行完毕后,如果目标方法有返回值,就向reply中写入返回值。
flag : 0
3.DESCRIPTOR
Binder的唯一标识,一般用当前AIDL接口名表示。
总之一切都是通过binder对象来完成,远程服务端持有binder对象实体,本地客户端Proxy持有服务端binder对象引用,并通过Sub.asInterface()方法转换成本地的接口类对象。