「Android Binder」AIDL中的 in / out 到底是啥?

用过aidl的同学,可能见过下面的写法:

interface IInterface {
   
    void foo0(in int input);
    void foo1(out IDTParcel parcel);
    void foo2(inout IDTParcel parcel);
}

不知道你有没有好奇过这里的 in / out / inout 是什么意思呢?

directional tag

官网一查,只找到一点点信息:

All non-primitive parameters require a directional tag indicating which way the data goes. Either in, out, or inout (see the example below).

Primitives, String, IBinder, and AIDL-generated interfaces are in by default, and cannot be otherwise.

Caution: You should limit the direction to what is truly needed, because marshalling parameters is expensive.

哦,原来这里的 in / out / inout 属于 directional tag (定向标签?)的概念,指的是which way the data goes (数据以哪种方式流动?),啥意思?从概念到解释都不是人话;不满意的你继续搜索相关博客…

directional tag 不是什么?

在说清楚它是什么之前,先聊聊directional tag不是什么:

如果你搜索aidl in out这几个关键词,会有很多文章出来,很多文章的结论是这样的:

AIDL中的定向 tag 表示了在跨进程通信中数据的流向

  • 其中 in 表示数据只能由客户端流向服务端
  • out 表示数据只能由服务端流向客户端
  • inout 则表示数据可在服务端与客户端之间双向流通。

Stack Overflow上也一样:

Here it goes,

  • Its only a directional tag indicating which way the data goes.
    • in - object is transferred from client to service only used for inputs
    • out - object is transferred from client to service only used for outputs.
    • inout - object is transferred from client to service used for both inputs and outputs.

(为了避免部分追求“效率”的读者只读关键词,文中错误的结论都会加中划线)

上面的结论听着很有道理,但你可能会发现一个问题:接口回调的场景无法实现了!

在aidl中,如果client向server注册一个Callback(如下代码所示),server会在某些场景回调client,这时候数据流向是server => client, 按照上面的逻辑,这个result数据无法到达client,因为int数据的directional tag只能是in(后面会讲到), 而in只能支持client到server的数据传输方向

//aidl file
interface ICallback {
   
    void onResult(int result);
}

//aidl file
interface IController {
   
    void registerCallback(ICallback callback);
}

但是,如果使用过AIDL,会发现接口回调是可以正常工作的(验证demo地址结果如下),否则我们早就发现这个高频使用场景的异常了。

D/directional tag: server register callback
D/directional tag: client onResult: 1

结论和事实有冲突,假设(上面的结论)一定有问题!

大家得出这个错误结论是情有可原的,毕竟对于大多数开发者,AIDL“听得多,用得少”,第一个人在写Demo验证的时候场景特殊,基于这个特殊场景得出的结论就是错误的。
其实这也是刺激我写下本文的原因,因为全网浏览量最高的博客(几乎)全都讲错了,真是生气又骄傲~

那么 directional tag 到底是什么呢?
下面我们就一步一步来验证:

源码之下

要弄清楚究竟发生了什么,源码之下毫无秘密。

为了避免部分同学一脸懵逼,这里补充一点关于AIDL的前置知识:

AIDL作为一种跨进程通信的方案,底层依赖Binder,跨进程通信时会调用AIDL中定义的方法,会把 caller(调用者,后文只用caller)的参数数据 copy 到 callee(接收者,后文只用callee),然后在callee进程中调用另外一个代理对象的相同方法,这个逻辑由Binder框架封装;使用者上层看起来,感觉是直接调用了对方进程中对象的方法。

AIDL文件在编译后会生成2个重要的实现类:

  • Stub
    callee被调用时,会通过Stub.onTransact(code, data, reply, flag)间接地调用本地对象(Local Binder)的对应方法。

  • Proxy
    caller调用AIDL方法时,最终通过Proxy调用remote.transact(code, _data, _reply, flag),然后通过Binder机制调用到远程的相应方法。

    上面的onTransact() 和 transact() 方法都是Binder定义的方法,更底层的跨进程逻辑由Binder机制实现,就不是本文的重点了。

有了这些基础知识,下面我们写一个AIDL文件,看一下对应的方法做了什么事情,全部代码请看这里

//aidl file: State
parcelable State;
//aidl file: IController
interface IController {
   
    int transIn(in State state);
    int transOut(out State state);
    int transInOut(inout State state);
}

AIDL文件IController编译后的关键代码如下:

in
//Proxy(caller)
public int 
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值