文章内容
一、Binder 为什么是面试必问题
1.1 大厂必问

Handler 和 Binder 是 Android 面试里出现频率最高的两道基础题,公司规模越大问得越细。Handler 还好,多数人写过 postDelayed、debug 过 ANR,多少有点感性认识;Binder 就不一样,工作三五年却讲不清它是什么的开发者相当多。
原因在于日常开发几乎接触不到它的存在——startActivity 一行代码调下去,中间隔着几次跨进程通信,编译器和框架把细节全藏起来了。
1.2 先弄清是什么,再谈为什么
面试官问 Binder,不是想听你背源码。这道题真正的筛选点有两个:你知不知道 Android 的组件模型是建立在跨进程通信之上的,以及你有没有在多进程架构里干过活。
这两点都要从"Binder 到底是什么"讲起。定义说不准,后面的动机也就无从谈起。
二、Binder 是什么:三个层次

这个问题可以从三个角度回答,三层都说到,答案才算完整。
2.1 整体:跨进程通信机制
Binder 是 Android 的 IPC(Inter-Process Communication,进程间通信)机制。Linux 本身提供了管道、消息队列、共享内存、socket 等多种 IPC 手段,Android 在此之外自己实现了 Binder,作为整个系统跨进程调用的统一通道。
上层看到的是"调用某个服务的方法",实际是一次跨越进程边界的数据传递和方法分派。
2.2 驱动层:/dev/binder 字符设备
往下看,Binder 是一个内核态的字符设备驱动,设备节点是 /dev/binder。进程通过 open 打开它、ioctl 收发数据,再由内核完成数据在两个进程地址空间之间的搬运。
它之所以比传统 IPC 高效,关键在于用 mmap 把内核缓冲区映射到接收方进程的用户空间,一次数据拷贝就能完成传递,而管道、socket 这类需要两次(发送方到内核,内核到接收方)。
2.3 应用层:Binder.java
在 Java 层,Binder 是一个类——android.os.Binder。用 AIDL(Android Interface Definition Language,安卓接口定义语言)写过跨进程接口的人对这个结构不会陌生:编译器根据 .aidl 文件生成一个 Stub 抽象类,而 Stub 继承自 Binder。服务端实现类只要继承这个 Stub,就具备了被其他进程调用的能力。
客户端拿到的则是 Proxy,它持有一个 IBinder 引用,方法调用被打包成 Parcel 数据丢进驱动。同一套机制,在应用层表现为类的继承关系,在内核层表现为设备文件的读写。
2.4 常见误区:内存管理不是答案
有人会答"Binder 是一套内存管理机制"。Binder 驱动确实做内存管理——mmap 映射、缓冲区分配、事务数据的存放都由它负责,一次事务的数据大小还有 1MB 左右的上限。
但这是它的实现细节,不是它的定位。被问"Binder 是什么"时答内存管理,等于把手段说成了目的,面试官会立刻判断你只读过零散的源码片段,没有整体认知。
三、为什么一线企业都用多进程
3.1 根因:单进程的堆内存上限

今日头条、微信、QQ、微博这类应用全是多进程架构,中小公司的 App 则常常一个进程跑到底。这个差别不是技术品味问题,而是被内存逼出来的。
Android 虚拟机给每个进程的 Java 堆设了硬上限,跟手机装了多少物理内存没有关系。设备总内存 16GB,单个进程的堆照样卡在系统属性规定的那个值上。在连着 adb 的设备上敲一条命令就能看到:
adb shell getprop dalvik.vm.heapgrowthlimit # 查询普通应用的堆增长上限,超过它就 OOMadb shell getprop dalvik.vm.heapsize # 查询 largeHeap=true 时能放宽到的上限视频里演示的设备返回 384m(示例值,各厂商 ROM 会按机型自行调整,常见范围从 128m 到 512m 不等)。这意味着一个单进程 App 无论怎么优化,Java 堆能用的就这么多,突破不了。
还有一层压力来自 LMK(Low Memory Killer,低内存杀手)。系统内存吃紧时,它按优先级和内存占用挑进程回收,占得越多越容易被选中。把应用拆小,本身就是一种保活手段。
3.2 场景一:内存大户单独开进程

图库浏览、大图解码、视频编辑这类功能,峰值内存动辄一两百兆。放在主进程里,一次相册加载就可能把整个应用推到 OOM 边缘。
拆出去单独跑一个进程,主进程和图库进程各自享有独立的 384MB 额度,加起来接近 768MB 可用堆——这是绕开单进程上限唯一有效的办法。功能用完进程退出,占用的内存立刻还给系统。
3.3 场景二:推送等常驻功能
推送 SDK 基本都要求跑在独立进程里,原因是稳定性。主进程的生命周期跟着用户操作走,用户一划退出,进程随时可能被回收,推送通道跟着断掉。
独立进程可以维持较低的内存占用和较高的存活优先级,主进程被杀了它照样在线。消息到达率是推送服务的核心指标,这个拆分几乎没有替代方案。
3.4 场景三:WebView 与风险隔离
WebView 是出了名的内存泄漏重灾区,某些系统版本上销毁后仍然持有 Activity 引用。把它关进独立进程,页面用完直接结束进程,泄漏跟着一起消失。微信就有一个专门的 tools 进程处理 WebView 相关的页面。
再往下推一层,任何不稳定的代码都值得隔离——第三方 SDK、native 库、复杂的解析逻辑。多进程的效果类似装了多个独立 App,其中一个崩溃,其余的照常运行,用户看到的只是某个页面打不开,而不是整个应用闪退。
3.5 怎么开:android:process
拆进程不需要写额外的启动代码,在 AndroidManifest.xml 里给组件加一个属性就行:
<application> <service android:name=".push.PushService" android:process=":push" /> <!-- 冒号开头表示应用私有进程,最终名为 包名:push --> <activity android:name=".web.WebActivity" android:process=":tools" /> <!-- 另一个私有进程,专门承载 WebView 页面 --> <service android:name=".core.SharedService" android:process="com.example.remote" /> <!-- 不带冒号是全局进程,同签名的其他应用可共享 --></application>属性加上之后,这个 Service 或 Activity 就跑在另一个进程里。随之而来的是:主进程和它之间再也不能直接传对象、共享静态变量,所有交互只能走跨进程通信——也就是 Binder。多进程架构和 Binder 是绑在一起的,选了前者就必须懂后者。
四、你以为没用过,其实天天在用

4.1 四大组件与系统服务
即使一个进程写到底,也躲不开 Binder。AMS(ActivityManagerService,活动管理服务)跑在 system_server 进程里,和你的应用进程分属两边。每次 startActivity、bindService、sendBroadcast,请求都要跨进程送到 AMS,AMS 处理完再反向调回应用进程执行生命周期。
PMS(PackageManagerService)管理应用安装与权限查询,WMS(WindowManagerService)负责窗口的添加和布局协商,它们同样住在 system_server 里。查一次 getPackageInfo、加一个悬浮窗,中间都是 Binder 事务。
4.2 系统功能同样跨进程
拨打电话交给 telephony 服务,设置闹钟交给 AlarmManagerService,剪贴板、通知栏、蓝牙、定位,全都是同一套路径:应用侧拿到一个 Proxy,方法调用被序列化成 Parcel,经 /dev/binder 送到对应的系统服务进程。
写在中小公司单进程 App 里的每一行组件代码,背后都在用 Binder,只是框架把它包得太严实,让人以为自己从没碰过。
五、总结
5.1 答题路径
被问到 Binder,按这个顺序说:
- 整体上它是 Android 的跨进程通信机制;
- 驱动层它是字符设备
/dev/binder,靠mmap做到一次拷贝; - 应用层它是
Binder类,AIDL 生成的Stub继承它,服务端继承Stub就获得了被跨进程调用的能力。
总之别把内存管理当定义,那是实现细节。
5.2 后续
Binder 往下还有很多内容:一次完整通信的数据流向、驱动如何做一次拷贝、ServiceManager 的角色、AIDL 生成代码里 transact 和 onTransact 的对应关系,这些留到后面几篇展开。
本文由 AgentPlanFlow 生成