一、Android系统架构全景:从Linux内核到应用框架的完整分层解析

说实话,我做了这么多年Android底层开发,每次带新人时都会先问一个问题:「你觉得Android系统到底长什么样?」 大部分人能说出「应用层、框架层、内核层」这几个词,但再往深了问,就说不清了。

今天我就带你把这层窗户纸捅破。咱们从下往上,一层一层拆开看。

1.1 整体架构:五层模型

Android系统架构,说白了就是五层结构。我习惯把它想象成一栋楼:

  • 底层地基:Linux内核
  • 承重墙:硬件抽象层(HAL)
  • 水电管道:系统运行时(ART、Native库)
  • 楼层管理:Java应用框架
  • 住户:应用层

每一层只跟上下两层打交道。这个设计很巧妙——你想想看,如果应用能直接操作内核,那系统早乱套了。

核心要点:Android不是纯粹的Linux,它只是「借用了」Linux内核。上层全是Google自己造的轮子。

应用层(Applications) 桌面、设置、浏览器、第三方App 应用框架层(Java API Framework) ActivityManager、WindowManager、ContentProvider、ViewSystem 系统运行时层(Android Runtime & Native Libs) ART、WebView、OpenGL、SQLite、SSL 硬件抽象层(HAL) Camera HAL、Audio HAL、Sensor HAL、GPS HAL Linux内核层(Linux Kernel) 进程调度、内存管理、驱动模型、Binder驱动、电源管理 用户空间 内核空间

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 各层之间的交互机制

层与层之间怎么通信?我总结了三句话:

  1. 应用→框架:通过Binder IPC调用系统服务
  2. 框架→Native:通过JNI调用C/C++库
  3. 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能跑在几千种不同硬件上的根本原因。


公众号:蓝海资料掘金营,微信 deep3321