aidl跨进程通信原理追踪

继续上一个demo,Proxy.add 加上日志:

在 Stub.onTransact 里加日志:

进程号就不打印了,因为之前已经测试过了,已经知道transact方法是运行在客户端进程,而Stub.onTransact是运行在服务端进程。

运行,关键日志:

可见,before transact 和 after transact 都是运行在客户端进程的主线程。而中间的onTransact是运行在服务进程,binder线程。而且transact是同步调用,它会等待服务端的onTransact调用结果。

问题:为什么 onTransact 跑到了另一个进程的 Binder:25891_4 线程上?因为transact 是一个同步的跨进程调用。客户端主线程会阻塞在 transact 上等结果;Binder 驱动把请求传递给服务端进程,服务端由 Binder 线程池里的一个线程执行 onTransact,执行完再把结果通过驱动送回客户端,主线程才从 transact 返回。

客户端,mRemote.transact 在 Java 层看起来是个普通方法调用,但 mRemote 是 BinderProxy,它的 transact 会走到 Native 层,最终进入 Binder 驱动。

而服务端进程启动时,主线程在跑 Looper,负责 UI、生命周期、消息循环。Binder 请求不由主线程处理,而是由 Binder 驱动维护的线程池处理。

流程总结:

1、客户端 transact 进入驱动。

2、驱动发现目标进程是 25891,把请求放到该进程的待处理队列。

3、驱动唤醒/创建一个 Binder 线程

4、该线程从Binder驱动读到请求,回调 Stub.onTransact。

5、onTransact 里调用我们写的那个add方法,把结果写进 reply。

6、线程把 reply 交回驱动。

7、驱动把 reply 送回客户端,唤醒主线程。

8、主线程从 transact 返回,打印 after transact。

画成图:

客户端进程 (pid 31216) 服务端进程 (pid 25891)

───────────────────── ─────────────────────

主线程 main

│

├─ before transact 日志

│

├─ BinderProxy.transact()

│ │

│ ▼

│ JNI -> Native

│ │

│ ▼

│ Binder 驱动 ──────────────────────► Binder 线程池

│ │ │

│ │ ├─ 创建/唤醒 Binder:25891_4

│ │ │

│ │ ├─ 回调 Stub.onTransact

│ │ │ │

│ │ │ ├─ onTransact 日志

│ │ │ ├─ 调用 add(1,2)

│ │ │ └─ 写 reply

│ │ │

│ │ ◄─────────────────────────────────┘

│ ▼

│ 主线程被唤醒

│

├─ after transact 日志

▼

继续执行

再把 transact 的最后一个参数改成 IBinder.FLAG_ONEWAY验证下,即改成这样:

复制代码
boolean _status = mRemote.transact(TRANSACTION_add, data, reply, IBinder.FLAG_ONEWAY);

运行:

日志:

Groovy 复制代码
---------------------------- PROCESS STARTED (11277) for package com.example.binderdemo ----------------------------
---------------------------- PROCESS STARTED (13445) for package com.example.binderdemo ----------------------------
2026-10-17 13:03:42.081 13445-13445 RemoteService2          com.example.binderdemo               D  onBind thread=main, pid=13445
2026-10-17 13:03:42.082 11277-11277 ClientMainActivity      com.example.binderdemo               D  onServiceConnected thread=main
2026-10-17 13:03:42.082 11277-11277 MiniProxy               com.example.binderdemo               D  before transact, thread=main
2026-10-17 13:03:42.082 11277-11277 MiniProxy               com.example.binderdemo               D  after transact, status=true,thread=main
2026-10-17 13:03:42.082 13445-13468 MiniStub                com.example.binderdemo               D  onTransact add, thread=Binder:13445_3
2026-10-17 13:03:42.082 13445-13468 RemoteService2          com.example.binderdemo               D  MiniStub.add(9527,0) thread=Binder:13445_3, pid=13445
2026-10-17 13:03:42.084 11277-11277 ClientMainActivity      com.example.binderdemo               D  当前客户端进程:11277
2026-10-17 13:03:42.084 11277-11277 ClientMainActivity      com.example.binderdemo               D  remote: com.example.binderdemo.MiniProxy@20cc485
2026-10-17 13:03:42.084 11277-11277 ClientMainActivity      com.example.binderdemo               D  service: android.os.BinderProxy@72e62da

result为0了。计算结果明显不对。看日志发现,after transact 紧跟 before transact,中间没有等。主线程没有阻塞在 transact 里。而且after transact 先打印,服务端 onTransact 后执行。客户端已经走完了,服务端才开始处理。所以这种情况客户端获取到的是默认值0。服务端仍然在 Binder:13445_3 线程执行,依然跨进程,变的只是"等不等结果"。

阻塞与否的分叉点在 IPCThreadState::transact 里,有这段代码:

cpp 复制代码
    if ((flags & TF_ONE_WAY) == 0) {
        // 同步调用
        if (reply) {
            err = waitForResponse(reply);        // 等 BR_REPLY,带回结果
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);   // 没有 reply 也等,确保事务完成
        }
    } else {
        // oneway 调用
        err = waitForResponse(nullptr, nullptr); // 也调用了 waitForResponse,但不等 reply
    }

画成图是这样:

复制代码
transact()
  │
  ├─ writeTransactionData(BC_TRANSACTION, ...)
  │
  ├─ 同步 (TF_ONE_WAY == 0)
  │     └─ waitForResponse(reply)
  │           ├─ 等 BR_TRANSACTION_COMPLETE(驱动确认)
  │           └─ 继续等 BR_REPLY(服务端结果)→ 返回
  │
  └─ oneway (TF_ONE_WAY != 0)
        └─ waitForResponse(nullptr, nullptr)
              └─ 只等 BR_TRANSACTION_COMPLETE(驱动确认)→ 返回

transact 在 Native 层根据 TF_ONE_WAY 标志决定是否进入 waitForResponse。同步调用进入 waitForResponse,它循环和驱动交互,直到收到 reply 才返回,所以 Java 层主线程阻塞在 transact 上。oneway 调用跳过 waitForResponse,发完请求立刻返回,服务端异步处理,客户端不等结果。这就是日志里 after transact 先于 onTransact 打印的原因。

相关推荐
zh_xuan1 天前
aidl 跨进程通信流程
aidl·跨进程通信
mmsx4 天前
Android 模块化三件套:轻量组件化、宿主插件双形态、AIDL 隔离
android·网络·组件化·aidl
阿pin2 个月前
Android随笔-AIDL
android·开发语言·aidl
与水同流4 个月前
Android13 AIDL HAL服务实现Demo
android·hal·aidl
齊家治國平天下5 个月前
Android 14 AIDL HAL 使用指南-获取服务流程解析
android·hal·aidl·servicemanager·aidl hal·获取服务
齊家治國平天下5 个月前
Android 14 AIDL HAL 使用指南-注册服务流程解析
hal·aidl·android14·servicemanager·aidl hal
阿亮爱学代码5 个月前
Service
运维·服务器·service·aidl·跨进程通信
哈哈,柳暗花明5 个月前
Framework开发专题系列四:Binder基础
framework·binder·aidl·messenger·hidl·native binder·asyncchannel
小书房6 个月前
Android Binder机制
binder·aidl