目录

AIDL 语法详细总结

AIDL 语法详细总结

AIDL(Android Interface Definition Language,Android接口定义语言)是Android系统基于Binder IPC机制设计的专用语法规范,用于定义客户端与服务端都认可的跨进程通信编程接口,编译器会自动生成标准化的Binder代理类与桩类,屏蔽底层跨进程通信的复杂实现。

官方核心说明:仅当你需要允许不同应用的客户端跨进程访问你的服务,并且需要在服务中处理多线程并发时,才需要使用AIDL。

一、AIDL 文件基础规范

  1. 文件后缀:必须以 .aidl 为后缀名,不可使用其他后缀。
  2. 编写规则:必须使用Java语法结构编写,支持单行注释//和多行注释/* */;除importpackage语句前的注释外,所有代码注释都会被同步到生成的IBinder接口中。
  3. 单文件限制:每个 .aidl 文件只能定义一个顶级接口一个parcelable类型声明,不可在单个文件中定义多个顶级接口/类型。
  4. 包名规范:AIDL文件的包名必须与对应Java/Kotlin实现类的包名完全一致,否则会出现编译期类找不到错误。
  5. 存放路径:Android Studio项目中默认存放路径为 src/main/aidl/,需与Java代码的包结构一一对应;跨模块/跨应用使用时,客户端与服务端的AIDL文件内容、包名必须完全一致。
  6. 导入规则:所有非默认支持的类型,即使与当前文件在同一个包下,也必须显式添加import语句,这是与Java语法最核心的区别之一。

二、AIDL 支持的完整数据类型

AIDL对数据类型有严格限制,仅支持以下6大类,非支持类型无法通过编译。

类型分类 具体支持范围 导入要求 官方核心约束
Java基本数据类型 byte、short、int、long、float、double、boolean、char 无需import 默认且仅支持in定向Tag,不可修改为其他类型
基础引用类型 String、CharSequence、IBinder 无需import 默认且仅支持in定向Tag,底层做了专属序列化优化
集合类型 List、Map 无需import 1. List:泛型参数必须是AIDL支持的类型,编译后实际接收类固定为ArrayList
2. Map:不支持参数化泛型(如Map<String,Integer>),key必须为String,value必须是支持的类型,实际接收类固定为HashMap,官方建议优先使用Bundle替代
AIDL生成接口 其他AIDL文件定义的接口类型 必须显式import 可直接作为方法参数/返回值,底层传递的是Binder引用,而非对象副本
Parcelable自定义类型 实现了Android Parcelable 序列化接口的类 1. 必须创建同名AIDL文件声明parcelable 类名;
2. 业务AIDL中必须显式import
自定义对象跨进程传递的唯一合法方式,AIDL声明文件与Java实现类的包名、类名必须完全一致
数组类型 以上所有支持类型的一维数组(如int[]User[]String[] 元素类型需import则数组同步需import 仅支持一维数组,不支持多维数组

⚠️ 官方明确禁忌:AIDL原生不支持枚举(enum)、静态内部类、未实现Parcelable的普通JavaBean,枚举类型必须用int常量替代。

三、核心语法规则与关键字详解

1. 接口定义语法

// 包声明,必须与Java实现类包名完全一致
package com.example.aidl;

// 按需导入依赖的类型,即使同包名也必须显式导入
import com.example.aidl.User;
import com.example.aidl.IOnDataChangeListener;

// 接口定义
interface IUserManager {
    // 常量定义 + 方法定义
}

核心规则:

  • 必须使用interface关键字定义,不可使用classenum等;
  • 不可使用publicprivateprotected等访问修饰符,AIDL接口默认全局可访问;
  • 支持接口继承,语法与Java一致(如interface IUserManager extends IBaseManager),但必须显式import父接口;
  • 接口内仅可定义方法常量,不可定义成员变量、构造方法、静态代码块等。

2. 常量定义语法

AIDL支持在接口内定义常量,仅支持int和String类型,语法如下:

interface IUserManager {
    // 完整写法,static final可省略,编译器自动补全
    static final int ERROR_USER_NOT_EXIST = -1;
    // 简写写法,支持const关键字
    const int ERROR_NO_PERMISSION = -2;
    String KEY_USER_ID = "user_id";
}
  • 常量默认是public static final,修饰符可省略;
  • 仅支持int和String类型,不支持其他引用类型常量。

3. 方法定义语法

// 基础格式:[oneway] 返回值类型 方法名(参数列表) [throws 异常];
// 示例
void addUser(in User user);
User getUserById(int userId) throws android.os.RemoteException;

核心规则:

  • 方法不可有实现体,必须以分号结尾,与Java接口方法一致;
  • 返回值可以是AIDL支持的任意类型,无返回值使用void
  • 参数规则:
    1. 除基本数据类型、String、CharSequence、IBinder外,所有引用类型参数必须显式添加定向Tag(in/out/inout),否则编译报错;
    2. 不支持可变参数...,不支持多维数组参数;
  • 异常声明:支持抛出RemoteException(跨进程通信必选异常),也支持抛出其他Java检查型异常;
  • 修饰符:不可使用staticfinalsynchronized等修饰符,仅支持oneway异步修饰符;
  • 扩展特性:可手动为方法指定交易代码,避免接口升级导致的方法编号错乱,语法为void getUser() = 10;
  • 空值注解:可空的参数或返回值必须使用@nullable注解标注。

四、定向Tag(in/out/inout)深度解析

定向Tag是AIDL特有的语法,用于标注跨进程参数的数据流向,本质是控制Binder驱动的序列化与反序列化行为,决定参数数据在客户端与服务端的传递方向,直接影响IPC性能。

核心视角:所有数据流向的描述,均以客户端传入的参数对象为参照。

定向Tag 官方核心定义 数据流向 底层行为细节 性能开销 适用场景
in 输入型参数(默认值) 客户端 → 服务端 1. 客户端将参数完整序列化后传递给服务端,服务端反序列化得到数据副本
2. 服务端对该参数的所有修改,不会同步回客户端
最低(单次序列化) 绝大多数场景:客户端向服务端传递数据,无需服务端回传修改结果
out 输出型参数 服务端 → 客户端 1. 客户端不会传递参数的初始值,服务端收到的是空的默认对象实例
2. 服务端对参数的修改,会在方法执行完成后序列化回传给客户端,同步更新客户端的原对象
中等(单次序列化) 客户端仅需服务端回传填充数据,无需传递初始值的场景(如获取默认配置对象)
inout 输入输出型参数 客户端 ↔ 服务端 双向 1. 客户端传递参数的完整初始值,服务端反序列化得到完整对象
2. 服务端对参数的所有修改,都会在方法执行后同步回传给客户端,双向更新
最高(双倍序列化) 极少数场景:同时需要传递初始值,又需要服务端回传修改结果

⚠️ 关键注意事项:

  1. 基本数据类型、String、CharSequence、IBinder、AIDL生成接口,默认且仅支持inTag,强制使用out/inout会直接编译报错;
  2. outinout仅支持Parcelable自定义类型、List、Map等引用类型,且对应的Parcelable类必须实现readFromParcel()方法,否则out/inout不生效,服务端修改无法回传;
  3. 官方强烈建议:必须将定向Tag限制在实际需要的范围内,滥用inout会带来严重的IPC性能损耗。

五、oneway 关键字完整用法与约束

oneway是AIDL特有的异步修饰符,用于标记非阻塞IPC调用,核心作用是让客户端调用远程方法时,将事务数据写入Binder驱动后立即返回,不阻塞等待服务端执行结果

1. 修饰规则

  • 修饰整个接口:oneway interface IEventNotify { ... },则接口内所有方法自动成为oneway异步方法;
  • 修饰单个方法:oneway void sendEvent(int eventId);,仅该方法为异步调用;
  • 不可同时修饰接口和单个方法,避免重复修饰。

2. 强制语法约束(违反直接编译报错)

  • oneway方法必须无返回值(返回值必须为void),异步调用无法同步返回结果;
  • oneway方法的参数只能使用in定向Tag,不可使用out/inout,无法回传数据;
  • oneway方法不可声明抛出任何检查型异常,包括RemoteException,异步调用无法同步抛出异常。

3. 核心特性与原理

  • 非阻塞特性:本地调用时,oneway无任何影响,调用仍为同步;仅跨进程远程调用时,才会触发非阻塞逻辑;
  • 执行顺序:同一个Binder服务的多个oneway调用,服务端会按调用顺序串行执行(Binder线程池的单一线程),不会并发执行,天然避免线程安全问题;而非oneway方法是并行执行的;
  • 线程模型:服务端的oneway方法依然运行在Binder线程池中,而非主线程,不可直接操作UI;
  • 可靠性:oneway调用不保证100%执行,若服务端进程意外死亡,客户端不会收到任何回调通知。

4. 适用场景

事件通知、状态上报、日志上报等单向通知场景,无需服务端返回结果,同时需要避免客户端主线程因IPC阻塞导致ANR。

六、Parcelable 自定义类型语法规范

自定义对象跨进程传递的唯一合法方式,是实现Parcelable接口,并配套对应的AIDL声明文件,必须严格遵循以下规范。

1. 第一步:创建AIDL声明文件

创建与Java类同名、同包名的AIDL文件,仅做parcelable类型声明,不可包含其他内容:

// User.aidl,包名必须与User.java完全一致
package com.example.aidl;

// 声明parcelable类型,关键字parcelable首字母小写
parcelable User;

2. 第二步:Java类实现Parcelable接口

必须实现完整的序列化、反序列化逻辑,同时为了支持out/inoutTag,必须实现readFromParcel()方法:

package com.example.aidl;

import android.os.Parcel;
import android.os.Parcelable;

public class User implements Parcelable {
    public int userId;
    public String userName;

    // 1. 内容描述方法,默认返回0即可
    @Override
    public int describeContents() {
        return 0;
    }

    // 2. 序列化:将数据写入Parcel
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(userId);
        dest.writeString(userName);
    }

    // 3. 反序列化:必须定义public static final的CREATOR常量
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            User user = new User();
            user.userId = in.readInt();
            user.userName = in.readString();
            return user;
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    // 4. out/inout Tag必须实现的方法:从Parcel读取数据回写到当前对象
    public void readFromParcel(Parcel in) {
        userId = in.readInt();
        userName = in.readString();
    }
}

3. 第三步:业务AIDL中导入使用

在业务接口AIDL文件中,显式import该Parcelable类型,即可作为方法参数/返回值使用,必须显式添加定向Tag。

七、进阶语法与特殊场景

1. 双向通信:回调接口

AIDL支持将另一个AIDL接口作为参数传递,实现服务端向客户端的反向调用,这是Android跨进程双向通信的标准实现方式。

步骤1:定义回调AIDL接口

// IOnDataChangeListener.aidl
package com.example.aidl;

import com.example.aidl.User;

interface IOnDataChangeListener {
    void onUserChanged(in User newUser);
    void onError(int errorCode, String errorMsg);
}

步骤2:主接口中定义注册/反注册方法

// IUserManager.aidl
package com.example.aidl;

import com.example.aidl.User;
import com.example.aidl.IOnDataChangeListener;

interface IUserManager {
    void registerListener(IOnDataChangeListener listener);
    void unregisterListener(IOnDataChangeListener listener);

    void addUser(in User user);
}

注意:回调接口的方法执行时,客户端默认运行在Binder线程池,如需操作UI,必须手动切换到主线程。

2. 稳定版AIDL(Stable AIDL)

Android 10(API 29)引入的稳定版AIDL,用于保证接口的向后兼容性,适用于系统服务、跨应用长期维护的IPC接口。

核心语法

// 开启稳定AIDL支持
@Backing(type="stable")
package com.example.aidl;

interface IStableUserManager {
    // 版本1原生方法
    void addUser(in User user);
    // 版本2新增方法,标注@Since注解
    @Since(2)
    void updateUser(in User user);
    // 弃用方法,标注@Deprecated注解
    @Deprecated
    void oldMethod();
}

配套gradle配置

android {
    buildFeatures {
        aidl true
    }
    aidl {
        version 2 // 接口版本号
        stability "stable" // 开启稳定模式
    }
}

稳定版AIDL会生成版本化的序列化代码,保证接口升级后,新旧版本的客户端与服务端可正常通信,不会出现方法编号错乱的问题。

八、语法禁忌与高频踩坑点

1. 绝对禁止的语法

  • 禁止在AIDL接口中定义成员变量、构造方法、静态代码块;
  • 禁止使用public/private/protected修饰接口和方法;
  • 禁止使用static/final/synchronized修饰方法;
  • 禁止使用枚举、未实现Parcelable的普通JavaBean作为参数/返回值;
  • 禁止oneway方法有返回值、使用out/inout参数、抛出检查型异常;
  • 禁止使用多维数组、可变参数...
  • 禁止在单个AIDL文件中定义多个顶级接口/parcelable声明。

2. 高频编译报错与踩坑点

  1. 同包名类型未显式import:AIDL的import规则与Java不同,哪怕同包名,也必须显式导入Parcelable类型和其他AIDL接口,否则编译报错;
  2. Parcelable文件与Java类包名不一致:必须保证包名、类名完全一致,否则会出现“找不到符号”错误;
  3. 引用类型参数未加定向Tag:除基本类型、String、CharSequence、IBinder外,所有引用类型必须显式添加in/out/inout,否则编译失败;
  4. out/inout参数未实现readFromParcel():导致服务端修改无法回传客户端,定向Tag不生效;
  5. 服务端方法在主线程执行:默认AIDL方法(包括oneway)均运行在Binder线程池,不可直接操作UI,必须保证线程安全;
  6. 跨应用AIDL文件不一致:客户端与服务端的AIDL文件包名、内容必须完全一致,否则会出现Binder事务编号错乱、通信失败。

九、完整语法示例

1. Parcelable类型声明:User.aidl

package com.example.aidl_demo;

parcelable User;

2. 回调接口:IOnDataCallback.aidl

package com.example.aidl_demo;

import com.example.aidl_demo.User;

interface IOnDataCallback {
    void onUserUpdate(in User newUser);
    void onError(int errorCode, String errorMsg);
}

3. 主业务接口:IUserManager.aidl

package com.example.aidl_demo;

// 显式导入所有依赖类型
import com.example.aidl_demo.User;
import com.example.aidl_demo.IOnDataCallback;

interface IUserManager {
    // 常量定义
    static final int ERROR_USER_NOT_EXIST = -1;
    static final int ERROR_NO_PERMISSION = -2;
    String KEY_DATA = "user_data";

    // 基础in定向Tag方法
    boolean addUser(in User user);

    // 带返回值+异常声明的方法
    User getUserById(int userId) throws android.os.RemoteException;

    // inout双向定向Tag方法
    void updateUser(inout User user);

    // out输出定向Tag方法
    void getDefaultUser(out User user);

    // oneway异步方法
    oneway void notifyUserActive(int userId);

    // 回调注册与反注册
    void registerCallback(IOnDataCallback callback);
    void unregisterCallback(IOnDataCallback callback);

    // 集合类型方法
    List<User> getAllUser();
    Map<String, User> getUserMap();
}