前言: 在日常 Android 开发中,我们习惯了用 AIDL 来定义跨进程接口。但 AIDL 本质上只是一个代码生成工具,它自动生成的 Stub 和 Proxy 类完全可以手写。理解手写 Binder 的过程,能帮助你深入掌握 Android IPC 机制的核心原理。
一、Binder 通信整体架构
在开始手写代码之前,我们先搞清楚 Binder 通信的核心角色。一次完整的跨进程调用,涉及四个关键组件:
客户端 App
↓ transact()
Proxy(代理类)
↓ Binder Driver
Stub(存根类)
↓ onTransact()
服务端实现
💡 核心原理: Proxy 负责把方法调用「打包」成 Parcel 数据,通过 Binder 驱动发送到服务端;Stub 负责「解包」Parcel,调用真正的业务实现,再把结果「打包」返回给客户端。
二、项目结构准备
本文的示例分为服务端和客户端两个模块。手写 Binder 不需要 AIDL 文件,但需要手动维护接口一致性。
📁 server (服务端)
📄 IMyManualService.java — 接口定义
📄 MyManualServiceStub.java — 存根抽象类
📄 MyManualServiceProxy.java — 代理实现类
📄 ManualBinderService.java — 服务端 Service
📁 client (客户端)
📄 MainActivity.kt — 绑定服务并调用
⚠️ 注意事项: 客户端需要将 IMyManualService、MyManualServiceStub、MyManualServiceProxy 这三个文件原样复制到客户端工程中,保证两端接口完全一致。
三、定义接口:IMyManualService
首先定义 AIDL 中常见的 IInterface 接口。这个接口包含两个核心要素:
- DESCRIPTOR:Binder 服务的唯一标识符,用于客户端查询本地接口
- 业务方法:定义服务端暴露给客户端调用的方法
IMyManualService.java
package com.qhsoft.foodsafety.module;
import android.os.IInterface;
import android.os.RemoteException;
public interface IMyManualService extends IInterface {
// 唯一标识符,用于区分不同的 Binder 服务
// 这个字符串必须全局唯一,建议用包名+接口名的全限定名
String DESCRIPTOR = \”com.qhsoft.foodsafety.module.IMyManualService\”;
// 定义远程方法:两数相加
int add(int a, int b) throws RemoteException;
// 定义远程方法:打招呼
String sayHello(String name) throws RemoteException;
}
💡 为什么继承 IInterface? IInterface 是 Android 为 Binder 接口定义的基础接口,它只有一个方法 asBinder(),用于获取底层的 IBinder 对象。所有 AIDL 生成的接口都继承自它。
四、手写 Stub:MyManualServiceStub
Stub 是 Binder 通信的服务端入口。它继承 Binder 并实现自定义接口,负责接收客户端发来的请求,并分发给具体的业务实现。
Stub 的核心职责
MyManualServiceStub.java
package com.qhsoft.foodsafety.module;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
public abstract class MyManualServiceStub extends Binder implements IMyManualService {
// 事务码:客户端和服务端必须完全一致
// 这里的 1、2 就是方法的\”身份证\”
static final int TRANSACTION_add = 1;
static final int TRANSACTION_sayHello = 2;
public MyManualServiceStub() {
// 将当前 Binder 对象和描述符绑定
// 这样客户端 queryLocalInterface 时才能找到它
this.attachInterface(this, DESCRIPTOR);
}
@Override
public IBinder asBinder() {



