Android面试冲击附答案(一)————Binder机制
一、下边问题都能答上来吗?答不上来下滑复习。
- 简述Binder机制核心流程
- 为什么Binder机制只需要一次拷贝?一次拷贝是从哪里拷贝到哪里?拷贝的是什么?
- ServiceManager是什么?怎么被找到的?一个进程有多个Binder服务时怎么区别?
- Intent为什么不能传递对象?同一个进程里,如果通过Binder机制来传递数据,还会序列化反序列化吗?
- 为什么Zygote和AMS通信使用Socket不用Binder?
- Binder线程池大小是多少?Binder线程池在什么时候创建的?
二、Binder机制原理
为什么需要IPC?
Android基于Linux,每个进程拥有独立的虚拟地址空间,进程间内存天然隔离,无法直接访问彼此的数据。IPC(进程间通信)就是解决这个问题的机制。
传统IPC的问题
传统IPC(如管道、Socket)需要两次数据拷贝:
- 发送方:用户空间 → 内核空间(第一次拷贝)
- 接收方:内核空间 → 用户空间(第二次拷贝)
效率低,且无法验证调用方身份,安全性差。
Binder的核心设计
Binder基于CS架构,由四个核心角色组成:
| 角色 | 说明 |
|---|---|
| Client | 发起IPC请求的进程 |
| Server | 提供服务,向ServiceManager注册 |
| ServiceManager | Binder注册中心,handle固定为0 |
| Binder驱动 | 运行在内核态,负责数据转发和地址映射,是通信核心 |
为什么只需要一次拷贝?
Binder驱动利用mmap内存映射实现:
- Binder驱动在内核空间分配一块缓冲区
- 通过mmap将这块内存同时映射到接收方的用户空间
- 发送方通过
copy_from_user将数据拷贝到内核缓冲区(唯一一次拷贝) - 接收方直接读取映射的内存,无需再次拷贝
mmap本质:把同一块物理内存,同时映射到两个虚拟地址空间。
Binder通信核心流程
Server ──注册服务──▶ ServiceManager
│
Client ──查询服务──▶ ServiceManager ──返回BinderProxy──▶ Client
Client ──调用方法──▶ Parcel序列化 ──▶ Binder驱动 ──copy_from_user──▶ 内核缓冲区
│ mmap
Server用户空间 ◀──读取
│
Client ◀──返回结果────────────────── Binder驱动 ◀──写回结果────────────────
代理模式的作用
不同进程的对象内存地址不通用,无法直接传递对象。Binder驱动在传递时做转换:
- Server进程持有真实的Binder对象
- Client进程拿到的是BinderProxy代理对象
- Proxy封装了序列化、transact、反序列化的全过程,对Client透明
安全性保障
- 内核层自动填充调用方的UID和PID,Client无法伪造
- Server可通过
Binder.getCallingUid()和Binder.getCallingPid()验证调用方身份 - 系统服务根据UID判断调用方是否有权限
三、面试题与答案
1. 简述Binder机制?相比传统IPC方式有什么优点?
Binder是Android核心的IPC机制,基于CS架构。
相比传统IPC的优点:
- 效率高:只需一次数据拷贝(传统IPC需要两次)
- 安全性高:内核自动填充调用方UID/PID,无法伪造身份
- 使用方便:通过AIDL封装,调用远程方法像调用本地方法一样
2. Binder机制核心成员和作用?
- Client:发起IPC请求
- Server:提供服务,启动后向ServiceManager注册
- ServiceManager:管理所有服务的注册与查询,handle固定为0
- Binder驱动:处于内核态,负责进程间数据转发和地址映射,是通信核心
3. 简述Binder机制核心流程
- Server启动后向ServiceManager注册服务
- Client通过handle=0找到ServiceManager,查询目标服务
- ServiceManager返回Server的代理对象(BinderProxy)给Client
- Client调用代理方法,将数据封装成Parcel,发起transact请求
- Binder驱动接收数据,
copy_from_user拷贝到内核缓冲区 - 内核缓冲区通过mmap映射到Server用户空间,Server直接读取
- Server处理完后,通过Binder驱动原路返回结果
补充:ServiceManager本身也通过Binder驱动注册,驱动设置其handle=0,全局唯一。
4. 为什么Binder机制只需要一次拷贝?(mmap的原理)
传统IPC需要两次拷贝:用户空间→内核空间→用户空间。
Binder通过mmap优化:
- Binder驱动在内核空间分配缓冲区
- 通过mmap将这块内存映射到接收方用户空间
- 发送方只需
copy_from_user拷贝一次到内核缓冲区 - 接收方直接读取映射内存,无需第二次拷贝
mmap本质:把同一块物理内存,同时映射到两个虚拟地址空间。
5. 一次拷贝是从哪里拷贝到哪里?拷贝的是什么?
通过copy_from_user方法,从发送方用户空间拷贝到Binder驱动在内核空间开辟的缓冲区。
拷贝的内容是发送方的Parcel数据,包括:
- 基本数据类型
- 序列化后的对象
- 文件描述符(fd)
6. ServiceManager是什么?怎么被找到的?
ServiceManager是Binder的注册中心,所有系统服务(AMS、WMS等)启动时都要向它注册。
怎么被找到:它的handle被Binder驱动固定设置为0,任何进程都可以直接通过handle=0找到它,不需要查询。
ServiceManager在init进程初始化时创建,运行在独立的用户空间,全局唯一。
7. 一个进程有多个Binder服务时怎么区别?
通过句柄handle区分。每个Binder服务在Binder驱动中都有唯一的handle,Client通过handle来定位具体的Server。
8. Binder对象在跨进程传递时是怎么处理的?
不直接跨进程传递,Binder驱动会做转换:
- 真实的Binder对象存在于Server进程中
- Client获取到的是BinderProxy代理对象
- Client通过代理对象生成Parcel发起请求,Binder驱动转发到真实的Binder对象处理
9. 为什么Binder机制需要使用代理模式?
- 不同进程间内存地址不通用,无法直接传递对象
- Proxy封装了跨进程的序列化、transact、反序列化全过程,对Client完全透明,调用远程方法就像调用本地方法
10. 同一个进程里,通过Binder机制传递数据,还会序列化反序列化吗?
不会。asInterface()内部通过queryLocalInterface()判断IBinder是否是本地Binder对象:
- 同进程:直接返回Stub,不走IPC,不序列化
- 跨进程:返回BinderProxy,走IPC,需要序列化
延伸:哪些是同进程IPC场景?
- 同APP内的
bindService() onServiceConnected回调里调用IMyService.Stub.asInterface(binder)
四大组件间的交互是同进程吗? 生命周期调度会绕一圈AMS,必然发生IPC。只有同进程的数据通信才能绕过IPC直接调用。
11. AIDL是什么?AIDL生成代码背后的通信过程?
AIDL(Android Interface Definition Language)是Android提供的IPC接口定义语言,底层基于Binder。
编译后生成Stub和Proxy两个类:
- Proxy(Client端):把调用参数写入Parcel,通过transact发送给Binder驱动
- Stub(Server端):继承Binder,实现
onTransact(),解析Parcel,执行方法,把结果写回Parcel返回
对Client来说,调用远程方法就像调用本地方法,跨进程细节完全被封装。
12. Binder线程池大小是多少?什么时候创建的?
- 普通APP进程:默认最大15个,Binder驱动根据需要动态创建,不是一开始就满员
- SystemServer进程:上限设为31个,因为要处理大量系统服务请求
创建时机:在ActivityThread.main()方法里启动。
13. Client如何查找到具体的Server?
- Server启动后向ServiceManager注册,ServiceManager维护名字→Binder对象的映射(如"activity"对应AMS)
- Client通过handle=0找到ServiceManager
- 告诉ServiceManager需要的服务名,ServiceManager返回对应的Binder对象
- Binder驱动在Client进程中为该对象分配一个handle
- Client后续通过这个handle与Server通信
14. Binder机制如何保证安全性?
- 内核层自动填充调用方的UID和PID,Client无法伪造
- Server可通过
Binder.getCallingUid()和Binder.getCallingPid()验证调用方身份 - 系统服务根据UID判断调用方是否有对应权限
15. 有哪些IPC方式?应用场景?
| 方式 | 场景 |
|---|---|
| Binder | Android核心IPC,系统服务通信 |
| AIDL | 基于Binder,支持并发的跨进程调用 |
| Messenger | 基于AIDL,Binder+Handler,单线程串行处理 |
| ContentProvider | 基于Binder,跨进程数据共享,大数据用Ashmem |
| Socket | 基于TCP/UDP,任意设备间通信,效率较低 |
| 共享内存 | 0次拷贝,效率最高,但实现复杂 |
| 管道Pipe | Linux原生,单向通信,logcat底层使用 |
16. 为什么Zygote和AMS通信使用Socket不用Binder?
核心原因:fork与多线程不兼容。
- Binder通信依赖多线程(Binder线程池)
fork()只复制当前线程,不复制其他线程- 如果fork前Binder线程池已启动,fork后子进程的线程池状态不完整,会导致死锁
- Socket是线程安全的,fork后可以正常使用
17. Intent传输数据上限?
一般是1MB到2MB。
这个大小对应Binder驱动在内核空间开辟的Binder缓冲区大小,这块内存是整个进程共享的,不是单次传输独占的。
传输大数据应使用:文件、ContentProvider 或共享内存(Ashmem)。