
前言:在日常 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.javapackage 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:MyManualServiceStubStub 是 Binder 通信的服务端入口。它继承Binder并实现自定义接口,负责接收客户端发来的请求,并分发给具体的业务实现。Stub 的核心职责定义事务码(TRANSACTION_code),每个方法对应一个唯一整型 ID实现asInterface(),判断同进程还是跨进程重写onTransact(),解析客户端数据并调用本地实现MyManualServiceStub.javapackage 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() {