2. Android系统架构与Native Service定位
Android 并不是一个“Linux + Java”的简单组合,而是一套自下而上严格分层的系统栈。理解这套分层结构,是理解 Native Service 为什么存在、放在哪里、由谁启动、如何被访问的前提。很多开发者第一次接触 Native Service 时,会把它当成“用 C++ 写的后台线程”,结果在权限、启动时机、Binder 注册上反复踩坑。根本原因,是没有先建立清晰的分层与定位认知。
一、Android 分层架构:五层职责划分
从下往上,Android 系统大致分为五层,每一层都有明确的职责边界与对外接口:
- Linux 内核层(Kernel):负责进程调度、内存管理、设备驱动、电源管理,并提供 Binder 驱动、ashmem、logger 等 Android 特有内核组件。Binder 驱动是整个 IPC 机制的“地基”。
- 硬件抽象层(HAL):以 .so 或 HIDL/AIDL 服务形式存在,向上屏蔽摄像头、音频、传感器等硬件差异,向下调用内核驱动。
- Native 层(C/C++ 库与 Native Service):包含 libc、libutils、libbinder、libcutils 等基础库,以及 surfaceflinger、audioserver、cameraserver、servicemanager 等核心 Native Service。这一层是系统能力的“真实实现层”。
- Java 框架层(Framework):以 system_server 为核心,提供 ActivityManagerService、PackageManagerService、WindowManagerService 等 Java 系统服务,并通过 JNI 调用 Native 层能力。
- 应用层(App):普通 APK 与系统应用,通过 Binder 代理对象访问框架层服务,间接使用 Native 能力。
这五层的关键特征是:上层依赖下层,但跨层访问必须通过明确定义的接口。Native Service 位于第三层,它既可能被 Java 框架层通过 JNI 调用,也可能直接向应用层暴露 Binder 接口。
二、Native 层的角色:系统能力的真实实现者
Native 层不是“框架层的附属”,而是系统能力的真实实现者。它的角色可以概括为三点:
- 能力提供者:图形合成、音频混音、相机采集、传感器采样等高性能、低延迟任务,几乎都由 Native Service 完成。Java 层更多是策略与调度。
- 接口暴露者:Native Service 通过 Binder 向 Java 层或应用层暴露接口。例如 SurfaceFlinger 通过 ISurfaceComposer 提供显示合成能力。
- 资源管理者:Native Service 通常以独立进程运行,持有硬件句柄、共享内存、文件描述符等稀缺资源,避免 Java 层直接操作。
关键认知:Java 框架层负责“决定做什么”,Native 层负责“真正怎么做”。两者通过 Binder 与 JNI 协作,而不是互相替代。
三、Binder IPC 机制概述
Native Service 之间、Native 与 Java 之间、应用与系统服务之间,几乎都依赖 Binder 通信。Binder 的核心角色有三个:
- Binder Driver:位于内核,负责数据拷贝、线程调度、引用计数与身份校验。
- ServiceManager:Native 层的服务注册与查询中心,服务启动后向它注册名称,客户端通过名称查询代理。
- Binder Proxy / Stub:客户端持有 Proxy,服务端实现 Stub,双方通过 transact 与 onTransact 完成调用。
一次典型的 Binder 调用流程如下:
客户端 Proxy.transact(code, data, reply)
↓
Binder Driver 转发(携带目标 handle 与调用线程)
↓
服务端 Binder 线程 onTransact(code, data, reply)
↓
服务端业务逻辑执行,结果写回 reply
↓
Binder Driver 将 reply 返回客户端
对于 Native Service 开发者,最常接触的是 BnInterface 与 BpInterface 模板,以及 defaultServiceManager() 获取 ServiceManager 代理。服务注册的典型代码如下:
sp<IServiceManager> sm = defaultServiceManager();
sm->addService(String16("my.native.service"), new MyService());
四、Native Service 在系统启动中的位置
Native Service 的启动时机非常关键,它决定了服务能否被后续组件正常访问。Android 启动大致经历:内核启动 → init 进程 → 解析 init.rc → 启动 servicemanager → 启动 surfaceflinger、audioserver 等 Native Service → 启动 zygote → 启动 system_server → 启动应用。
| 启动阶段 | 关键进程/服务 | 与 Native Service 的关系 |
|---|---|---|
| init 阶段 | init、servicemanager | servicemanager 必须先启动,否则服务无法注册 |
| Native 服务阶段 | surfaceflinger、audioserver、cameraserver | 核心 Native Service 在此阶段启动并注册 |
| zygote 阶段 | zygote、zygote_secondary | 为 Java 层提供进程孵化能力 |
| system_server 阶段 | AMS、PMS、WMS | Java 系统服务通过 Binder 访问 Native Service |
| 应用启动阶段 | Launcher、普通 App | 通过框架层间接使用 Native 能力 |
实践提示:自定义 Native Service 通常通过 init.rc 中的 service 段启动,并设置 class core 或 class main。若服务依赖 servicemanager,应确保启动顺序正确,否则会出现 addService 失败或客户端查询不到服务。
五、常见误区与复盘
- 误区一:把 Native Service 当成普通线程。它通常是独立进程,拥有独立地址空间与权限,崩溃不会直接拖垮 system_server,但会影响依赖它的模块。
- 误区二:忽略 SELinux 与权限。Native Service 的注册、访问、文件操作都受 SELinux 策略约束,缺少策略会导致 avc denied。
- 误区三:启动顺序随意。servicemanager 未就绪时注册服务会失败;依赖的 HAL 未启动时,Native Service 初始化可能阻塞。
- 误区四:混淆 Binder 线程与主线程。Binder 回调运行在 Binder 线程池中,不是主线程,涉及共享状态时要注意加锁与生命周期。
总结来看,Android 分层架构决定了 Native Service 的“位置”,Native 层角色决定了它的“职责”,Binder IPC 决定了它的“通信方式”,而系统启动顺序决定了它的“启动时机”。把这四点串起来,才能在后续开发中准确判断一个 Native Service 应该放在哪里、由谁启动、如何暴露接口、以及如何被安全访问。