3. AIDL 基础语法:接口定义、数据类型、定向 tag 与 oneway 关键字
在深入多 Camera HAL 场景之前,必须牢固掌握 AIDL 的核心语法。AIDL(Android Interface Definition Language)是 Android 系统中用于跨进程通信(IPC)的接口定义语言。本章将系统讲解 AIDL 的接口定义规则、支持的数据类型、参数定向 tag(in/out/inout)以及 oneway 关键字的语义与使用场景。
3.1 AIDL 接口定义基础
AIDL 接口定义语法与 Java 接口类似,但扩展名为 .aidl。每个 AIDL 文件定义一个接口,接口内可以声明多个方法。
3.1.1 基本语法结构
// ICameraDevice.aidl
package com.example.camera.hal;
interface ICameraDevice {
// 打开相机设备
int open(String cameraId);
// 开始预览
void startPreview();
// 停止预览
void stopPreview();
// 关闭相机设备
void close();
}
关键规则:
- 文件扩展名必须为
.aidl。 - 包名必须与文件所在目录结构一致。
- 接口名通常以
I开头,遵循大驼峰命名法。 - 方法声明中,参数和返回值类型必须是 AIDL 支持的数据类型。
- 非基本类型参数必须指定定向 tag(in/out/inout)。
最佳实践: 在 HAL 层多 Camera 场景中,建议每个 Camera 设备对应一个独立的 AIDL 接口,如
ICameraDevice0、ICameraDevice1,便于管理和扩展。
3.1.2 接口继承
AIDL 支持接口继承,子接口可以继承父接口的方法。
// IBaseCamera.aidl
package com.example.camera.hal;
interface IBaseCamera {
int getCameraId();
String getCameraInfo();
}
// ICameraDevice.aidl
package com.example.camera.hal;
interface ICameraDevice extends IBaseCamera {
int open(String cameraId);
void startPreview();
void stopPreview();
void close();
}
3.2 AIDL 支持的数据类型
AIDL 对数据类型有严格限制,只支持以下类型:
| 类别 | 支持类型 | 说明 |
|---|---|---|
| 基本类型 | boolean, byte, char, short, int, long, float, double |
与 Java 基本类型一致,无需定向 tag |
| 字符串 | String |
默认 in 方向,可指定 out/inout |
| 字符序列 | CharSequence |
与 String 类似,用于文本序列 |
| 列表 | List<T> |
T 必须是 AIDL 支持的类型,如 List<String> |
| 映射 | Map<K, V> |
K 和 V 必须是 AIDL 支持的类型 |
| Parcelable | 自定义 Parcelable 类 | 需在 AIDL 文件中声明 parcelable 定向 |
| AIDL 接口 | 其他 AIDL 接口类型 | 用于回调或服务引用 |
注意: AIDL 不支持 Java 的
Date、BigDecimal 等复杂类型。如果需要传递复杂对象,必须实现 Parcelable 接口并在 AIDL 中声明。
3.2.1 Parcelable 自定义类型
在 HAL 多 Camera 场景中,经常需要传递自定义数据结构,如相机配置参数、帧信息等。此时需要定义 Parcelable 类。
// CameraConfig.java
package com.example.camera.hal;
import android.os.Parcel;
import android.os.Parcelable;
public class CameraConfig implements Parcelable {
public int width;
public int height;
public int format;
public int fps;
public CameraConfig(int width, int height, int format, int fps) {
this.width = width;
this.height = height;
this.format = format;
this.fps = fps;
}
protected CameraConfig(Parcel in) {
width = in.readInt();
height = in.readInt();
format = in.readInt();
fps = in.readInt();
}
public static final Creator<CameraConfig> CREATOR = new Creator<CameraConfig>() {
@Override
public CameraConfig createFromParcel(Parcel in) {
return new CameraConfig(in);
}
@Override
public CameraConfig[] newArray(int size) {
return new CameraConfig[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(width);
dest.writeInt(height);
dest.writeInt(format);
dest.writeInt(fps);
}
}
在 AIDL 文件中声明 Parcelable 类型:
// ICameraDevice.aidl
package com.example.camera.hal;
parcelable CameraConfig;
interface ICameraDevice {
int configure(CameraConfig config);
}
3.3 定向 tag:in / out / inout
定向 tag 是 AIDL 中非常重要的概念,用于指定参数在 IPC 过程中的数据传输方向。它直接影响跨进程通信的性能和语义。
3.3.1 三种定向 tag 对比
| 定向 tag | 数据流向 | 序列化行为 | 性能开销 | 典型使用场景 |
|---|---|---|---|---|
in |
客户端 → 服务端 | 客户端序列化后发送,服务端反序列化读取 | 低(单向序列化) | 传递配置参数、命令等只读数据 |
out |
服务端 → 客户端 | 服务端序列化后返回,客户端反序列化读取 | 低(单向序列化) | 获取状态、结果等输出数据 |
inout |
双向 | 客户端序列化发送,服务端反序列化修改后,再序列化返回 | 高(双向序列化) | 需要修改并返回输入参数的场景 |
3.3.2 语法示例
// ICameraService.aidl
package com.example.camera.hal;
import com.example.camera.hal.CameraConfig;
import com.example.camera.hal.CameraFrame;
interface ICameraService {
// in:客户端传入配置,服务端只读
int configure(in CameraConfig config);
// out:服务端填充帧数据,客户端读取
int captureFrame(out CameraFrame frame);
// inout:客户端传入参数,服务端修改后返回
int adjustParameters(inout CameraConfig config);
}
性能建议: 在多 Camera 高帧率场景中,尽量使用
in 或 out 而非 inout,因为双向序列化会增加 IPC 延迟和 CPU 开销。例如,传递帧数据时使用 out 方向,传递配置时使用 in 方向。
3.3.3 定向 tag 选择决策流程
3.4 oneway 关键字
oneway 关键字用于修饰 AIDL 接口方法,表示该方法调用是异步的,客户端调用后立即返回,不等待服务端处理完成。
3.4.1 oneway 语义
| 特性 | 普通方法 | oneway 方法 |
|---|---|---|
| 调用行为 | 同步阻塞,等待服务端返回 | 异步非阻塞,立即返回 |
| 返回值 | 可以有返回值 | 必须为 void |
| 异常处理 | 可以抛出异常 | 不能抛出异常 |
| out/inout 参数 | 支持 | 不支持(无返回值) |
| 典型场景 | 查询状态、获取数据 | 通知、事件回调、日志 |
3.4.2 语法示例
// ICameraCallback.aidl
package com.example.camera.hal;
interface ICameraCallback {
// 普通方法:同步查询
int getCurrentState();
// oneway 方法:异步通知
oneway void onFrameAvailable(int frameId);
oneway void onError(int errorCode, String errorMsg);
oneway void onCameraDisconnected();
}
3.4.3 多 Camera 场景中的 oneway 应用
在 HAL 多 Camera 场景中,oneway 关键字常用于以下场景:
- 帧到达通知: 当某个 Camera 设备产生新帧时,通过 oneway 回调通知上层,避免阻塞帧处理流水线。
- 错误事件上报: Camera 设备出现异常时,异步上报错误码,不影响其他 Camera 的正常工作。
- 状态变更通知: 如 Camera 设备热插拔、配置变更等事件,使用 oneway 实现广播通知。
性能优势: 在多 Camera 高并发场景中,使用 oneway 方法可以显著降低 IPC 延迟。例如,3 个 Camera 同时上报帧到达事件时,oneway 调用可以避免线程阻塞和死锁风险。
3.4.4 oneway 与定向 tag 的配合
由于 oneway 方法没有返回值,因此不能使用 out 和 inout 定向 tag。所有参数必须为 in 方向(基本类型默认 in)。
// 正确:oneway 方法只使用 in 参数
oneway void notifyEvent(in String eventName, in int eventCode);
// 错误:oneway 方法不能使用 out 参数
// oneway void getResult(out int result); // 编译错误
// 错误:oneway 方法不能有返回值
// int onewayMethod(); // 编译错误
3.5 综合示例:多 Camera HAL 接口定义
以下是一个综合示例,展示如何将本章知识点应用于多 Camera HAL 场景:
// IMultiCameraHal.aidl
package com.example.camera.hal;
import com.example.camera.hal.CameraConfig;
import com.example.camera.hal.CameraFrame;
import com.example.camera.hal.ICameraCallback;
interface IMultiCameraHal {
// 同步方法:查询 Camera 数量
int getCameraCount();
// 同步方法:打开指定 Camera(in 参数)
int openCamera(in String cameraId, in CameraConfig config);
// 同步方法:关闭指定 Camera
void closeCamera(in String cameraId);
// 同步方法:捕获帧(out 参数)
int captureFrame(in String cameraId, out CameraFrame frame);
// 同步方法:调整参数(inout 参数)
int adjustParameters(in String cameraId, inout CameraConfig config);
// oneway 方法:注册回调
oneway void registerCallback(in ICameraCallback callback);
// oneway 方法:注销回调
oneway void unregisterCallback(in ICameraCallback callback);
}
设计建议: 在实际 HAL 开发中,建议将同步方法(如打开、关闭、配置)和异步方法(如回调通知)分离到不同的 AIDL 接口中,保持接口职责单一,便于维护和测试。
3.6 本章小结
- 接口定义: AIDL 文件使用
.aidl扩展名,支持接口继承,包名必须与目录结构一致。 - 数据类型: 支持基本类型、String、CharSequence、List、Map、Parcelable 和 AIDL 接口类型。
- 定向 tag:
in(客户端→服务端)、out(服务端→客户端)、inout(双向),选择时需权衡性能与语义需求。 - oneway 关键字: 实现异步非阻塞调用,适用于事件通知、回调等场景,不能有返回值或 out/inout 参数。