核心原因
Binder需要进程已经存在
Binder的工作原理
┌─────────────────┐ ┌─────────────────┐
│ 进程 A │ │ 进程 B │
│ │ │ │
│ ┌───────────┐ │ Binder │ ┌───────────┐ │
│ │ BpXXX │──┼─────────┼──│ BnXXX │ │
│ │ (代理端) │ │ │ │ (stub端) │ │
│ └───────────┘ │ │ └───────────┘ │
│ │ │ │ │ │
│ │ │ │ ▼ │
│ ▼ │ │ ┌───────────┐ │
│ ┌───────────┐ │ │ │ 服务端 │ │
│ │ 服务端 │◄─┼─────────┼──│ 实现 │ │
│ │ 在进程A │ │ │ │ │ │
│ └───────────┘ │ │ └───────────┘ │
└─────────────────┘ └─────────────────┘
关键点 :Binder通信需要两个进程都存在 !
进程A需要先存在,才能注册Binder服务
进程 B 需要先存在,才能获取到进程 A 的 Binder 代理
问题来了:创建进程时,哪个进程都不存在!
情况:用户点击图标,要启动微信
│
▼
需要创建一个全新的进程来运行微信
│
▼
但是!Binder 需要进程已经存在才能通信
│
▼
矛盾!要创建进程需要通信,但通信需要进程已存在
为什么必须用 Socket?
Socket + fork 的工作原理
┌────────────────────────────────────────────────────────────────────┐
│ AMS (已存在的进程) │
│ │
│ 1. AMS 通过 Socket 发送命令给 Zygote │
│ socket.send("start:com.tencent.mm") │
│ │ │
└───────────────────────────┼────────────────────────────────────────┘
│ Socket 连接(Zygote 已监听 socket)
▼
┌────────────────────────────────────────────────────────────────────┐
│ Zygote (已存在的进程) │
│ │
│ 2. Zygote 收到命令,调用 fork() 创建新进程 │
│ │
│ pid = fork(); // Linux 系统调用 │
│ │
│ 子进程(微信):PID = 0(fork 返回值) │
│ 父进程(Zygote):PID = 微信的 PID(fork 返回值) │
│ │
│ │ │
│ │ fork() 后子进程继承 socket 连接 │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 微信进程(全新创建) │ │
│ │ │ │
│ │ - 继承 Zygote 的内存空间(写时复制) │ │
│ │ - 继承打开的文件描述符(包括 socket) │ │
│ │ - 然后 exec() 加载微信代码 │ │
│ │ │ │
│ │ 微信进程创建成功后,就可以使用 Binder 了 │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────┘
Socket vs Binder 对比

完整的进程创建流程
第一步:AMS 通过 Socket 通知 Zygote
// AMS 发送启动命令
Process.start("com.tencent.mm", // 进程名
"android.app.ActivityThread", // 入口类
app.uid, app.gids, ...);
// Process.start() 内部
private static ProcessStartResult start(...) {
// 1. 通过 socket 发送命令到 Zygote
return Zygote.startChildProcess(
socket, // Zygote 的 socket 描述符
processName, entryPoint, ...
);
}
第二步:Zygote fork 出新进程
// ZygoteServer.runSelectLoop() 收到命令后
Runnable runSelectLoop() {
// 接收 AMS 的命令
ZygoteConnection connection = acceptCommandPeer();
// 处理命令,fork 子进程
return connection.processOneCommand(this);
}
// ZygoteConnection.processOneCommand()
int pid = forkAndSpecialize(uid, gids, ...);
if (pid == 0) {
// 子进程(微信)中执行
// 这里子进程已经可以用了!
return handleChildProcess();
}
第三步:新进程初始化并使用 Binder
// 子进程(微信)执行
ActivityThread.main() {
// 1. 创建 Looper
Looper.prepareMainLooper();
// 2. 绑定到 AMS(这时可以用 Binder 了!)
IActivityManager mgr = ActivityManager.getService();
mgr.attachApplication(mAppThread); // ← Binder 通信
// 3. 进入消息循环
Looper.loop();
}
为什么 Binder 不能用于进程创建?
Binder 的前提条件
Binder 通信的必要条件:
│
├─► 1. 两个进程都必须已经存在
│
├─► 2. 服务端必须已经注册到 Binder 驱动
│
├─► 3. 客户端必须能够获取到服务端的引用
│
└─► 4. 需要通过 /dev/binder 设备进行通信
创建新进程时的问题
┌─────────────────────────────────────────────────────────────┐
│ 创建新进程场景 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 问题:我们要创建一个全新的进程 │
│ │
│ 但是: │
│ - 新进程不存在 → 无法建立 Binder 连接 │
│ - 无法获取新进程的 Binder 引用 │
│ - /dev/binder 还没准备好 │
│ │
│ 结论:Binder 在进程创建阶段完全无法使用! │
│ │
└─────────────────────────────────────────────────────────────┘
Socket 为什么可以?
Socket 的优势:
│
├─► 1. Zygote 进程已经存在,并且监听着 socket
│
├─► 2. Socket 不需要对方进程存在,只需要对方在监听
│
├─► 3. Socket 可以触发 fork() 系统调用
│
├─► 4. fork() 可以在 Zygote 进程中创建新的进程
│
└─► 5. 新进程继承 Zygote 的 socket 连接
总结

核心设计
-
Socket = 用于 触发进程创建 (因为可以调用 fork())
-
Binder = 用于 进程间通信 (进程创建后使用,效率更高)
这是 Android 系统设计的精妙之处: 用 Socket 创建进程,用 Binder 通信 ,两者各司其职!