目录:
- 1. 什么是 AIDL?
- 2. 为什么要用 AIDL?
- 3. VirtualAPP AIDL 使用
- 4. IPC 总线(IPCBus)动态扩展
- 5. 总结
1. 什么是 AIDL?
AIDL: Android Interface Definition Language, 即 Android 接口定义语言。
2. 为什么要用 AIDL?
Android 中每个应用都是独立的进程,拥有自己的虚拟机,虚拟地址,应用之间的内存不止不能互相访问,存在应用隔离,因此两个应用不能像面向对象语言一样直接进行接口的调用。两个进程之间的调用叫做 IPC(进程间通信)。Android 中进程之间的 IPC 调用有:管道、共享内存、消息队列、信号量、socket、binder,从性能、安全角度最终选择了 Binder.
3. VirtualAPP AIDL 使用
我们以获取已安装的应用列表为例。
3.1 AIDL
IAppManager.aidl:
package com.android.dockercore.server;
import com.android.dockercore.remote.InstalledAppInfo;
interface IAppManager {
// ...
List<InstalledAppInfo> getInstalledApps(int flags);
// ...
}
InstallAppInfo.aidl:
package com.android.dockercore.remote;
parcelable InstalledAppInfo;
3.2 Java
InstalledAppInfo 对应的 java 类:
public final class InstalledAppInfo implements Parcelable {
public String packageName;
public String apkPath;
public String libPath;
public boolean dependSystem;
public int appId;
public InstalledAppInfo(String packageName, String apkPath, String libPath, boolean dependSystem, boolean skipDexOpt, int appId) {
this.packageName = packageName;
this.apkPath = apkPath;
this.libPath = libPath;
this.dependSystem = dependSystem;
this.appId = appId;
}
public File getOdexFile() {
return VEnvironment.getOdexFile(packageName);
}
public ApplicationInfo getApplicationInfo(int userId) {
return VPackageManager.get().getApplicationInfo(packageName, 0, userId);
}
public PackageInfo getPackageInfo(int userId) {
return VPackageManager.get().getPackageInfo(packageName, 0, userId);
}
public int[] getInstalledUsers() {
return DockerCore.get().getPackageInstalledUsers(packageName);
}
public boolean isLaunched(int userId) {
return DockerCore.get().isPackageLaunched(userId, packageName);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.packageName);
dest.writeString(this.apkPath);
dest.writeString(this.libPath);
dest.writeByte(this.dependSystem ? (byte) 1 : (byte) 0);
dest.writeInt(this.appId);
}
protected InstalledAppInfo(Parcel in) {
this.packageName = in.readString();
this.apkPath = in.readString();
this.libPath = in.readString();
this.dependSystem = in.readByte() != 0;
this.appId = in.readInt();
}
public static final Creator<InstalledAppInfo> CREATOR = new Creator<InstalledAppInfo>() {
@Override
public InstalledAppInfo createFromParcel(Parcel source) {
return new InstalledAppInfo(source);
}
@Override
public InstalledAppInfo[] newArray(int size) {
return new InstalledAppInfo[size];
}
};
}
再看看编译生成的 IAppManager java 类:
public interface IAppManager extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.dockercore.server.IAppManager {
private static final java.lang.String DESCRIPTOR = "com.android.dockercore.server.IAppManager";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.dockercore.server.IAppManager interface,
* generating a proxy if needed.
*/
public static com.android.dockercore.server.IAppManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.android.dockercore.server.IAppManager))) {
return ((com.android.dockercore.server.IAppManager) iin);
}
return new com.android.dockercore.server.IAppManager.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 TRANSACTION_getInstalledAppInfo: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
com.android.dockercore.remote.InstalledAppInfo _result = this.getInstalledAppInfo(_arg0, _arg1);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.android.dockercore.server.IAppManager {
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 com.android.dockercore.remote.InstalledAppInfo getInstalledAppInfo(java.lang.String pkg, int flags) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.android.dockercore.remote.InstalledAppInfo _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(pkg);
_data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_getInstalledAppInfo, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.android.dockercore.remote.InstalledAppInfo.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getInstalledAppInfo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
public com.android.dockercore.remote.InstalledAppInfo getInstalledAppInfo(java.lang.String pkg, int flags) throws android.os.RemoteException;
}
3.3 如何添加 IBinder
3.4 获取 IInterface 进行使用
4. IPC 总线(IPCBus)动态扩展
AIDL 虽然让跨进程通信变得很简单,但无法实现运行时,动态扩展功能。每次 AIDL 的接口变化,都需要重新编译。在 VirtualApp 里,通过 IPC 总线,实现了运行时动态的扩展能力。
AIDL 自动生成的 Stub 和 Stub.Proxy 的主要功能:
- 生成 code,如TRANSACTION_getPid, TRANSACTION_getMyData,用于传输方法名的传输
- 传输方法名 (即 code) 和方法参数,调用真正实现类对应的方法
通过 IPC 总线实现这两个能力后,就可以实现运行时增加通信能力,整体框架如下:
5. 总结
客户端要获取 IBinder 对象有两种方式:
- 异步获取:通过绑定 Service 获取
- 同步获取:通过 ContentProvider 获取
整体过程如下图所示:
参考: