一、Android系统架构全景:从Linux内核到应用框架的完整分层解析
说实话,我做了这么多年Android底层开发,每次带新人时都会先问一个问题:「你觉得Android系统到底长什么样?」 大部分人能说出「应用层、框架层、内核层」这几个词,但再往深了问,就说不清了。
今天我就带你把这层窗户纸捅破。咱们从下往上,一层一层拆开看。
1.1 整体架构:五层模型
Android系统架构,说白了就是五层结构。我习惯把它想象成一栋楼:
- 底层地基:Linux内核
- 承重墙:硬件抽象层(HAL)
- 水电管道:系统运行时(ART、Native库)
- 楼层管理:Java应用框架
- 住户:应用层
每一层只跟上下两层打交道。这个设计很巧妙——你想想看,如果应用能直接操作内核,那系统早乱套了。
核心要点:Android不是纯粹的Linux,它只是「借用了」Linux内核。上层全是Google自己造的轮子。
1.2 Linux内核层:真正的幕后英雄
很多人以为Android内核就是标准Linux内核。嗯,这里要注意——它其实是Linux的「魔改版」。
Google加了不少私货:
- Binder驱动:进程间通信的核心。我当年调试一个跨进程死锁问题,追了三天才发现是Binder线程池耗尽。从那以后,我写代码都会留意Binder调用链的长度。
- wakelock(唤醒锁):电源管理的命根子。App申请了wakelock不释放?系统就别想休眠了。这问题我踩过坑——有个第三方SDK偷偷持锁,导致手机待机一晚上掉电30%。
- Low Memory Killer:低内存时杀进程的策略。跟标准Linux的OOM Killer不一样,它更激进,按优先级杀。
避坑指南:我曾经在移植新内核时,忘了把Binder驱动编译进去。结果系统启动后,所有跨进程调用全部卡死。嗯,那次加班到凌晨两点。
1.3 硬件抽象层(HAL):把硬件「藏」起来
HAL存在的意义是什么?说白了就是让上层不用关心底层硬件是谁家的。
举个例子:你写一个相机App,调用的是Camera API。至于底层是索尼的传感器还是三星的传感器,跟你没关系。HAL把差异全抹平了。
HAL的接口定义在 hardware/libhardware/include/hardware/ 目录下。每个硬件模块都是一个共享库(.so),通过 hw_module_t 结构体暴露接口。
// 典型的HAL模块结构
typedef struct hw_module_t {
uint32_t tag; // 必须为 HARDWARE_MODULE_TAG
uint16_t module_api_version;
uint16_t hal_api_version;
const char *id; // 模块ID,如 "camera", "audio"
const char *name;
struct hw_module_methods_t *methods;
} hw_module_t;
我个人习惯在写HAL层代码时,先定义好接口,再实现具体硬件逻辑。这样换硬件时,只需要换底层实现,上层不用动一行代码。
1.4 系统运行时层:Java与Native的桥梁
这一层有两个关键角色:
- ART(Android Runtime):负责把Java字节码编译成机器码。Android 5.0之前是Dalvik,之后换成ART。区别?ART在安装时就编译好了,Dalvik是运行时解释执行。所以ART应用启动更快,但安装包更大。
- Native库:C/C++写的核心库,比如WebView、OpenGL、SQLite。Java代码通过JNI调用它们。
你想想看,为什么Android要用Java写上层,底层却用C++?因为Java开发效率高,C++运行效率高。各取所长嘛。
关键点:ART和Native库运行在同一个进程空间里。这意味着Java代码可以直接访问Native内存——当然,这也意味着一个Native崩溃就能带走整个App。
1.5 应用框架层:开发者的「工具箱」
这一层是Android SDK的核心。我每天打交道最多的就是它。
主要组件包括:
| 组件 | 职责 | 个人经验 |
|---|---|---|
| ActivityManagerService | 管理Activity生命周期、任务栈 | AMS是系统里最繁忙的服务,我见过它因为日志太多导致ANR |
| WindowManagerService | 管理窗口层级、触摸事件分发 | 窗口层级搞错了,触摸事件就会「穿透」到下层 |
| PackageManagerService | 管理App安装、权限、组件信息 | PMS启动慢是Android开机慢的主要原因之一 |
| ContentProvider | 跨进程数据共享 | 用ContentProvider做IPC,比直接Binder调用安全得多 |
这些服务都运行在 system_server 进程里。这个进程一旦挂了,手机就会重启——也就是我们常说的「软重启」。
1.6 各层之间的交互机制
层与层之间怎么通信?我总结了三句话:
- 应用→框架:通过Binder IPC调用系统服务
- 框架→Native:通过JNI调用C/C++库
- Native→内核:通过系统调用(syscall)进入内核空间
举个例子:你点一下屏幕,触摸事件是怎么传递的?
1. 硬件中断 → Linux内核输入子系统
2. 内核 → /dev/input/eventX 节点
3. EventHub 读取原始事件
4. InputReader 解析成触摸事件
5. InputDispatcher 分发给目标窗口
6. ViewRootImpl 把事件传给View树
7. 你的 onTouchEvent() 被调用
整个过程跨越了内核空间→Native层→Java框架层→应用层。每一步都有缓冲区、有排队、有优先级判断。我调过触摸延迟问题,最后发现是InputDispatcher的队列太长,把优先级调高就好了。
注意:跨层调用是有代价的。每次Binder调用大约耗时几十微秒,频繁调用会拖慢UI。我见过有人在一个滑动事件里调了十几次Binder,结果卡成PPT。
1.7 小结
Android架构的精髓就四个字:分层隔离。
每一层只做自己的事,不越界。内核管硬件,HAL管抽象,Runtime管执行,Framework管服务,App管业务。
这种设计的好处是:换掉任何一层,其他层都不受影响。你可以换内核、换HAL实现、甚至换Runtime——只要接口不变,上层代码不用改。
嗯,这就是Android能跑在几千种不同硬件上的根本原因。