第2章 基本数据类型传递

2.1 原理

AIDL(Android Interface Definition Language)底层依赖Binder驱动完成进程间数据搬运。基本数据类型(int、long、boolean、float、double、String、CharSequence)是Parcelable的原子单元,Binder内核会直接将这些值复制到目标进程的堆栈中,不涉及复杂对象图遍历或引用计数。传递效率最高,且无需额外序列化注解。

映射关系:AIDL中的基本类型直接对应Java/C++原生类型,底层通过Parcel.writeInt()、writeLong()等系列API写入共享内存块,接收端按顺序读取。

String和CharSequence本质是字符数组封装,但在Binder传输时被归为“基本类型”的优化路径——内核会特判字符串长度并直接拷贝字符数据,避免二次序列化。

性能要点:基本类型传递没有堆内存分配(短字符串除外),延迟通常小于0.1ms,适合高频控制信号。

2.2 实现流程

从定义接口到进程间调用的标准步骤:

  1. 定义 .aidl 接口文件:声明需要传递的方法及基本类型参数。
  2. 实现 Stub 类:在服务端继承自动生成的 Stub 并重写方法。
  3. 暴露 Binder:在 onBind() 中返回 Stub 实例。
  4. 客户端绑定服务:通过 bindService() 获取远程接口代理。
  5. 发起调用:直接调用代理方法,传递基本类型数据。
关键注意:所有基本类型方法声明前无需 in/out/inout 定向修饰符——Binder 会自动按值传递。

2.3 代码实现

2.3.1 AIDL 接口定义

创建 IBaseDataType.aidl:

// IBaseDataType.aidl
package com.example.aidlbasic;

interface IBaseDataType {
    int calculate(int a, long b, float c, double d, boolean flag);
    String echoString(String input, CharSequence cs);
    void noReturn(int value);
}

编译器生成 IBaseDataType.java 接口和 Stub 内部类。

2.3.2 服务端实现

// BasicService.java
public class BasicService extends Service {
    private final IBaseDataType.Stub binder = new IBaseDataType.Stub() {
        @Override
        public int calculate(int a, long b, float c, double d, boolean flag) {
            return flag ? (int)(a + b + c + d) : (int)(a * b);
        }
        @Override
        public String echoString(String input, CharSequence cs) {
            return "Echo: " + input + " | " + cs.toString();
        }
        @Override
        public void noReturn(int value) {
            android.util.Log.d("BasicService", "Received value: " + value);
        }
    };
    @Override
    public IBinder onBind(Intent intent) { return binder; }
}

2.3.3 客户端调用

// MainActivity.java
private IBaseDataType remoteService;
private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        remoteService = IBaseDataType.Stub.asInterface(service);
        if (remoteService != null) {
            try {
                int result = remoteService.calculate(10, 100L, 3.14f, 2.718, true);
                String echo = remoteService.echoString("Hello", "World");
                remoteService.noReturn(42);
            } catch (RemoteException e) { e.printStackTrace(); }
        }
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        remoteService = null;
    }
};
// 在合适时机调用:bindService(intent, connection, Context.BIND_AUTO_CREATE);

2.4 调试

启用 Binder 事务日志可观察基本类型传递的数据包大小:

adb shell su -- sh -c "echo 1 > /d/binder/failsafe_threshold"
adb shell dumpsys binder > /sdcard/binder_dump.txt
# 搜索 "transaction" 和 "data_size" 看基本类型调用

Logcat 中日志关键字:Binder: transaction、Parcel。通过 android.util.Log 在 Stub 方法内打印入参。

调试陷阱:基本类型传参在 RemoteException 发生时不可继续使用代理对象,必须重新绑定。

2.5 部署与稳定性

类型跨进程安全约束常见问题
int/long无溢出检查,需业务层控制范围大数导致服务端计算溢出
float/double大小端自动转换(Android一律小端)精度损失(float转double)
boolean底层存储为 int(0/1)误传 null 或非 0/1 值
String/CharSequence最大长度受 Binder 缓冲区限制(1MB)超长字符串抛出 TransactionTooLargeException

稳定性建议:基本类型业务中避免 String 长度超过 1KB(高频调用时),否则应考虑本地缓存或分块传输。

2.6 常见错误

  • TransactionTooLargeException:超长字符串或传入大量数据(基本类型本身小,但多次调用会导致堆满)。
  • RemoteException: dead object:服务端进程异常退出,客户端旧代理不可用。
  • ClassCastException(极少):asInterface() 传入非预期的 IBinder 对象。
  • String 为 null 时未判空:Stub 实现中对 null 参数没有防御性检查,导致 NPE。
最佳实践:在 .aidl 方法声明后添加注释说明参数范围;服务端 Stub 实现内始终对 String/CharSequence 做 null 判断。

2.7 性能与优化

基本数据类型传递是 AIDL 中最轻量的操作,但仍需关注:

  • 单次调用开销:约 10–30 μs(包含 Binder 上下文切换),因此高频控制(如每帧更新 UI 坐标)建议合并成一次传递一个包含多个基本类型的结构体(自定义 Parcelable)。
  • 内存拷贝:每次调用会拷贝所有参数到内核缓冲区,大量参数可改用 Bundle 或 List<> 批量传输。
  • 避免不必要的 float→double 转换:接口参数类型应与实际数据类型严格匹配,否则底层会隐式转换消耗 CPU。
  • 异步调用:对于不关心返回结果的 void 方法,使用 oneway 关键字提升吞吐量(一对一请求不再等待)。

2.8 基本类型传递流程总图

基本数据类型传递流程 客户端代理调用 remoteService.calculate(...) Binder 驱动 Parcel.writeInt / writeLong ... 服务端 Stub 处理 onTransact → 执行方法 返回结果(基本类型值) 客户端 Parcel 构建 writeInt(10) writeLong(100L) writeFloat(3.14f) 内核缓冲复制 线性内存拷贝 无序列化/反序列化 大小端自动转换 服务端 Parcel 读取 readInt() → a readLong() → b readFloat() → c 基本类型 int, long, boolean, float, double, String, CharSequence 均通过 Parcel 直接读写,无堆分配(短字符串除外)