Android Framework Binder

0 阅读5分钟

Android Binder 跨进程通信深度解析|从设计者视角 + 源码全流程拆解

前言

做 Android 开发久了,大家都知道 App、AMS、WMS、SurfaceFlinger 之间靠 Binder 通信,但很多人只停留在代理、驱动、一次拷贝的死记硬背层面。

真正想吃透 Framework,必须换个思路:假设我是系统设计者,我为什么要做 Binder?我要解决什么问题?架构怎么分层?模块如何协作?

本文站在设计者视角 + 真实源码流程,完整拆解 Binder 架构、通信流程、一次拷贝原理、oneway 异步机制,附带精准源码路径与代码讲解。

一、为什么 Android 非要自研 Binder,不用 Socket?

  1. 性能问题:Socket 两次用户态 <-> 内核态拷贝,Binder 仅一次拷贝
  2. 安全问题:Binder 内核层自带 UID/PID 权限校验,恶意进程无法随意调用系统服务
  3. 易用性:代理 - 桩模式,上层调用和普通本地方法无感知

二、Binder 四层架构 + 精准源码路径 + 核心代码

1. 应用层 Java(上层调用入口)

核心接口:IInterface
  • 源码路径frameworks/base/core/java/android/os/IInterface.java
public interface IInterface {
    // 返回当前Binder对象,是跨进程调用的唯一凭证
    public IBinder asBinder();
}

讲解:所有 Binder 服务接口都必须继承该接口,AIDL 自动生成的代理类、服务类都实现该方法,用于获取 Binder 通信对象。

序列化容器 Parcel
  • 源码路径frameworks/base/core/java/android/os/Parcel.java核心方法:
// 写入Binder对象,用于跨进程传递服务引用
public final void writeStrongBinder(IBinder val)
// 读取Binder对象
public final IBinder readStrongBinder()
// 序列化基本数据、Intent等参数
public void writeInt(int val)
public int readInt()

讲解:跨进程不能直接传对象,Parcel 是 Android 专属序列化工具,比 Serializable 快,Binder 调用全靠它打包 / 解析参数。


2. Native 层 C++(Binder 通信核心实现)

客户端代理 BpBinder
  • 源码路径frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // 委托IPCThreadState发起真正的Binder调用
    status_t status = IPCThreadState::self()->transact(
        mHandle, code, data, reply, flags);
    return status;
}

讲解:App 侧所有 Binder 调用最终走到这里,transact是客户端发起 IPC 的核心入口,把请求交给线程状态管理类。

服务端桩 BBinder
  • 源码路径frameworks/native/libs/binder/BBinder.cpp
status_t BBinder::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // 空实现,由AMS/WMS等具体服务重写该方法,处理业务逻辑
    return NO_ERROR;
}

讲解:服务端接收 Binder 请求的统一入口,AMS、WMS、SurfaceFlinger 都会重写该方法,解析参数并执行自身业务。

线程 / 进程管理 IPCThreadState / ProcessState
  • IPCThreadState 路径frameworks/native/libs/binder/IPCThreadState.cpp
  • ProcessState 路径frameworks/native/libs/binder/ProcessState.cpp
// 每个Binder线程一个实例,管理收发数据
IPCThreadState* IPCThreadState::self()
// 整个进程全局唯一,打开Binder驱动,获取驱动文件句柄
ProcessState* ProcessState::self()

讲解

  • ProcessState:进程启动时打开 /dev/binder 驱动,全局单例;
  • IPCThreadState:每个 Binder 线程独立实例,维护线程收发缓冲区,管理线程池。

3. Binder 内核驱动层(性能 & 安全核心)

  • 源码路径drivers/android/binder.c(Linux 内核)
核心内存映射(一次拷贝关键)
static int binder_mmap(struct file *file, struct vm_area_struct *vma)
{
    // 把内核缓冲区,直接映射到服务端用户空间
    // 实现:客户端1次拷贝,服务端0次拷贝
}

讲解:Binder 高性能的根源,客户端用户空间→内核缓冲区1 次拷贝,内核缓冲区通过 mmap 直接映射到服务端,无需二次拷贝。

权限校验 & 线程调度
// 校验调用方UID/PID,做权限管控
current->cred->uid
// 唤醒服务端空闲Binder线程处理请求
wake_up_interruptible(&thread->wait);

4. ServiceManager 服务注册中心

  • 源码路径frameworks/native/cmds/servicemanager/binder.c核心逻辑:
  1. AMS/WMS 启动时调用 addService 注册自身 Binder;
  2. App 调用 getService 查询获取服务代理。

三、完整 Binder 调用全流程(以 App 调用 AMS.startActivity 为例,逐节点源码)

步骤 1:Java 层客户端发起调用(代理类)

  • 路径frameworks/base/core/java/android/app/ActivityManagerProxy.java
@Override
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    try {
        // 写入服务标识
        data.writeInterfaceToken(IActivityManager.DESCRIPTOR);
        // 序列化所有启动参数
        data.writeStrongBinder(caller);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        // 核心:发起Binder跨进程调用
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        return result;
    } finally {
        data.recycle();
        reply.recycle();
    }
}

讲解:AIDL 自动生成的代理类,把启动参数序列化,调用底层 Binder。

步骤 2:Native 层 BpBinder 转发,客户端线程阻塞

BpBinder→IPCThreadState,通过驱动把数据发给服务端,客户端线程阻塞等待。

步骤 3:内核驱动转发数据,唤醒服务端 Binder 线程

内核层binder_thread_read接收数据,唤醒 SystemServer 进程空闲 Binder 线程。

步骤 4:服务端 AMS 接收请求,重写 onTransact 处理

  • 路径frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javaAMS 的 Native 侧 Binder 接收请求,最终回调 Java 层 AMS 的 startActivity 核心逻辑:
@Override
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    return ActivityStarter.getInstance().execute(...);
}

步骤 5:执行完成,结果原路返回客户端

服务端把结果打包进 Parcel,经驱动传回,客户端解除阻塞,调用完成。

四、oneway 异步机制源码解析

  • 路径frameworks/native/libs/binder/IPCThreadState.cpp核心标志位:TF_ONE_WAY
if (flags & TF_ONE_WAY) {
    // 不阻塞等待服务端返回结果,客户端直接返回
    mOut.writeInt(BC_TRANSACTION);
    mOut.writeInt(...);
    return NO_ERROR;
}

讲解

  1. oneway 方法客户端调用后不阻塞
  2. 服务端在线程池异步处理;
  3. 不能有返回值、不能有 out 出参;
  4. AMS/WMS 核心管控逻辑不用 oneway,广播、通知场景大量使用。

五、总结(源码视角)

Binder 整套架构,对应源码核心链路:

  1. 应用层:IInterface + Parcel,定义接口、序列化参数;
  2. Native 层:BpBinder (客户端代理) + BBinder (服务端桩) + IPCThreadState (线程管理);
  3. 内核层:binder.c 驱动,实现一次拷贝、权限校验、线程调度;
  4. 服务注册:ServiceManager 统一注册查找系统服务;

这套机制,支撑了 Android 系统从开机到所有 App 运行的全部跨进程通信。