系统架构
先直接上图:

源码结构
~ tree
.
├── abi # (Application Binary Interface) 定义不同CPU架构下的应用二进制接口
├── art # (Android Runtime) Android运行时,包含DVM和ART的核心实现
├── bionic # Android的C库,包含libc, libm, libdl等
├── bootable # 启动相关代码,包括bootloader, recovery等
│ └── recovery # 恢复模式的实现
├── build # 编译系统核心,包含make文件和脚本
│ └── soong # 新一代的基于Go的编译系统
├── cts # (Compatibility Test Suite) 兼容性测试套件,用于验证设备是否符合Android规范
├── dalvik # Dalvik虚拟机(已被ART取代,但保留部分文件)
├── development # 开发工具和示例代码
├── device # 特定设备的配置文件,包含硬件厂商的定制内容
│ ├── common # 通用配置
│ └── google # Google设备的配置 (如Pixel)
├── external # 开源依赖库,如SQLite, Freetype, WebKit等
├── frameworks # Android核心框架层
│ ├── av # 音视频框架,包括Stagefright等
│ ├── base # 核心框架,包含ActivityManager, WindowManager等系统服务和API
│ ├── native # 原生框架层,提供C/C++ API
│ └── support # Android支持库(已演进为AndroidX)
├── hardware # 硬件抽象层 (HAL) 的实现
│ └── libhardware # HAL标准接口定义
├── kernel # Linux内核源码(需要单独下载或链接)
├── libcore # 核心Java库,如java.lang, java.util的实现
├── ndk # (Native Development Kit) 原生开发套件
├── out # 编译输出目录
│ ├── host # 主机端(PC)编译产物
│ └── target # 目标设备(手机)编译产物
├── packages # 系统应用和服务
│ ├── apps # 系统自带应用,如Settings, Launcher, Contacts
│ ├── providers# 内容提供者,如DownloadProvider, MediaProvider
│ └── services # 系统服务,如Telephony, SystemUI
├── prebuilts # 预编译的工具链和库,如交叉编译器
├── sdk # SDK和相关工具
├── system # 底层系统组件和服务
│ ├── core # 核心系统工具和服务,如init, adbd
│ ├── extras # 额外工具,如su
│ └── sepolicy # SELinux安全策略配置
├── test # 测试相关代码
├── toolchain # 工具链
└── tools # 开发和调试工具
编译AOSP
Mac Silicon编译
建议直接使用ubuntu构建,不踩坑
1.下载XCode (注意MacOS版本需要满足XCode要求)
2.创建大小写敏感磁盘并挂载:hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 150g ~/android.dmg && hdiutil attach ~/android.dmg -mountpoint /Volumes/android
3.下载源码同步工具:cd /usr/local/bin && sudo curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > repo && sudo chmod +x repo
4.同步指定版本:repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-11.0.0_r45 && repo sync
6.开始编译:
# 修改文件数限制
ulimit -S -n 2048
source build/envsetup.sh
make clobber
lunch
make -j8
错误处理
错误1
若出错现类似下面的错误:
.....
error: Unable to fully sync the tree.
error: Checking out local projects failed.
Failing repos:
prebuilts/tools // 重点
prebuilts/clang/host/darwin-x86
Try re-running with "-j1 --fail-fast" to exit at the first error.
就把失败的repos目录删掉,重新同步
错误2
显示cc_test.go:434: "Could not find a supported mac sdk: [\"10.10\" \"10.11\" \"10.12\" \"10.13\" \"10.14\" \"10.15\"]"是它判断了MacOS SDK版本,该SDK和xcode是绑定的,有如下解决办法:
1.下载对应版本的xcode,就是根据上面的错误信息看看哪些sdk符合要求,再看哪个版本的xcode自带sdk满足要求
2.若已有的sdk版本与所需版本差别不大,可强制修改判断列表,具体的先执行cat $(xcrun --show-sdk-path)/SDKSettings.json查看sdk版本,再修改build/soong/cc/config/x86_darwin_host.go里的darwinSupportedSdkVersions数组
3.直接去下载旧版的sdk,下载并解压到sdk目录后,需要修改/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/info.plist文件里的MinimumSDKVersion值为新的sdk版本号,注意该文件需要用xcode打开(或用VSCode+Binary Plist插件)
cd .repo/manifests
git branch -a |cut -d / -f 3
repo init -b android-9.0.0_r3
repo sync
repo sync --force-sync device/generic/mini-emulator-mipsrepo sync
注:如果空间还不够,可用
hdiutil resize -size 200g ~/android.dmg.sparseimage去扩容
下载android studio后,直接运行AVD会显示CPU不支持VT,需要先根据Tools -> SDK Manager -> SDK Tools (tab) -> Deselect 'Android Emulator' -> OK删除之前下载模拟器,再打开AVD重新下载正确的版本,此时就可以选择arm的镜像了
若遇到avd启动后黑/白屏,可尝试更换镜像
修改和编译
真正要开发还是应该用ubuntu,会少很多麻烦,正常的编译流程会很顺利:
## 先同步一个分支
### 下工具
mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
### 同步
# https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/
export
## 切分支
cd .repo/manifests
git branch -a | cut -d / -f 3
repo init -b imx-4.1.15-1.0.0_ga
repo sync # 如果不需要与服务器数据一致,可以不运行该步
repo start imx-4.1.15-1.0.0_ga --all
repo branches
为我在当前目录生成一个android kernel module的hello demo,它使用kleaf(Driver Development Kit)框架,其中内核源码目录在/home/tellmewhy/work/common-android-mainline,内核编译命令为tools/bazel run //common-modules/virtual-device:virtual_device_x86_64_dist
镜像系统
之前多为yaffs2格式,可用mkyaffs2image生成,unyaffs解压 ,在android5之后主流为ext4格式,分为raw ext4,它可以直接mount -o loop挂载,而sparse ext4是更常见的格式,需要用simg2img转成raw后挂载,再之后逻辑分区出现,要使用lpunpack解包并使用lpmake打包,下面分别说明:
1.boot.img:在安卓13之前包含 Linux内核(zImage) 和 ramdisk.img,之后只包含内核(GKI内核),负责系统初始化,它会刷入boot分区,由Bootloader加载,启动后不再修改,运行时只读
2.boot-init.img:这是安卓13引入的,13把ramdisk移到这个单独的镜像中了,它和设备相关,而GKI内核在boot.img中是通用的,这样便于维护和升级
3.userdata.img:存储用户数据,包括安装的APK(/data/app)、用户配置(/data/data)等,启动时以读些权限挂载到 /data
4.system.img:存储Android核心系统文件,包括系统应用(/app)、库文件(/lib|/lib64)、配置文件(/etc)、核心框架(/framework)、字体(/fonts)和系统二进制文件(/bin|/xbin)等,解压后对应设备的/system目录,Android10之前会只读挂载到/system下,之后引入system-as-root(SAR机制)后,直接挂载为/
5.system_ext.img:Android11引入的,用于分离厂商/运营商扩展功能,实现模块化更新,它也是以只读方式挂载,含厂商特有APP(/priv-app)、特有配置文件(/etc)、厂商特有库(/lib|lib64)和特有框架(/framework)等,厂商可以利用它扩展系统的能力
6.ramdisk.img:提供启动初期的临时文件系统,包含init可执行文件、init.rc脚本等,内核启动后解压到内存,不直接挂载为磁盘,需用cpio解压,无法直接挂载
7.super.img:Android 10+引入,整合system.img、vendor.img、product.img等逻辑分区,支持灵活调整分区大小,需用lpunpack解析为子镜像
8.recovery.img:独立系统,支持OTA更新、恢复出厂设置等,刷入recovery分区,通过组合键进入,运行时只读,但可擦除数据分区
9.cache.img:存储临时数据(如OTA更新包),加速系统运行,启动时挂载到 /cache,读写挂载内容可随意擦除
10.vendor.img:Android8引入的,用来存放硬件厂商(OEM)的硬件支持组件(用户态),包括硬件抽象层(HAL)驱动、闭源库文件、设备专属服务等,便于厂商在不修改aosp的情况下适配设备驱动,它会被挂载到 /vendor
11.vendor_dlkm.img:专用于存储厂商提供的可动态加载内核模块(如 GPU 驱动 gpu_drm.ko、加密模块 qce50.ko,为内核态哦)
12.vbmeta.img:Android 7.0+引入,用于验证分区完整性(如boot/system/vendor),刷入vbmeta分区,由Bootloader读取
启动过程
Android的启动过程从设备加电开始,经历多个阶段,最终进入系统桌面。
Bootloader
Bootloader是设备通电后运行的第一段软件代码,它的启动和执行构成了信任链的起点。
- 硬件信任根 (Hardware Root of Trust):设备加电后,CPU会执行一段固化在芯片内部ROM中的代码(Boot ROM)。这段代码由芯片制造商写入,无法修改,因此构成了硬件信任根。它的首要任务是加载并验证下一阶段的Bootloader。它会使用硬编码在芯片中的公钥,验证主Bootloader(通常位于eMMC/UFS的特定分区)的数字签名,确保其未被篡改。
- Bootloader加载与硬件初始化:验证通过后,Boot ROM将主Bootloader加载到RAM中执行。Bootloader接管控制权后,会进行第一阶段的硬件初始化,包括设置CPU时钟频率、初始化内存控制器(DRAM)和闪存等。
- 验证启动分区 (Android Verified Boot - AVB):这是信任链的关键一环。在加载内核之前,Bootloader会执行AVB流程:
- 加载vbmeta: Bootloader首先读取
vbmeta分区。这是一个元数据分区,包含了用于验证其他分区(如boot,system,vendor)的哈希树摘要和签名。 - 验证签名: Bootloader使用存储在设备熔断丝(eFuse)或特定只读区域的公钥,来验证
vbmeta自身的签名。这个公钥的私钥由OEM厂商保管。 - 验证分区:
vbmeta验证通过后,Bootloader会利用其中的哈希数据,逐一校验boot.img、system.img等重要分区的完整性。它会计算boot.img的哈希值,并与vbmeta中记录的哈希值进行比对,对于system.img它只验证信任根,剩下的会交由dm-verity去验证。
- 加载vbmeta: Bootloader首先读取
- 移交控制权:只有当
boot.img等所有关键分区都通过验证后,Bootloader才会将CPU的控制权交给内存中已解压的Linux内核。如果任何一个环节验证失败,Bootloader根据是否解了OEM锁(BL锁)来决定中止启动进入恢复模式,或是显示警告信息。
内核启动
内核的启动和初始化过程延续了信任链。此时,虽然内核自身已经被Bootloader验证,但它还需要确保即将挂载的系统分区的完整性。
- dm-verity / fs-verity:在挂载
system、vendor等关键分区时,内核会利用dm-verity(块设备验证)或fs-verity(文件级验证)机制来确保文件系统的完整性。- 哈希树 (Hash Tree):在构建系统镜像时,会为
system等分区生成一个哈希树。树的根哈希(root hash)被签名并存储在vbmeta分区中。 - 按需验证:当内核需要读取文件系统的一个数据块时,
dm-verity会首先计算该数据块的哈希,并沿着哈希树向上验证,直到与之前已验证过的根哈希进行比对。 - 工作机制:这个验证是透明且按需进行的。如果某个数据块的哈希与树中记录的不符,说明该块已被篡改,内核会立即返回一个I/O错误,阻止应用读取到被篡改的数据。这确保了即使设备已被root,也无法持久化地修改
/system分区的内容。
- 哈希树 (Hash Tree):在构建系统镜像时,会为
- 加载模块签名:如果内核需要加载外部模块(Kernel Module,
.ko文件),它会配置为只加载经过正确签名的模块,防止恶意驱动被加载,这里的签名是Linux的可加载模块签名机制。 - 启动init进程:完成所有必要的初始化和验证后,内核会启动位于根文件系统(ramdisk)中的
init进程,将控制权从内核空间移交到用户空间。
init 启动
init是Linux内核在完成自身初始化后,在用户空间(User Space)启动的第一个进程,因此它的PID(进程ID)永远是1。作为所有进程的“祖先”,init负责引导和管理整个Android用户空间的启动过程。它分为两个阶段,第一阶段在ramdisk里负责加载/system分区等,之后主要是第二阶段在工作,它的核心职责可以概括为以下几点:
1.解析.rc脚本:init启动后,会解析一系列以.rc为后缀的配置文件。这些脚本定义了系统启动的全部流程,包括挂载哪些文件系统、启动哪些原生服务(Daemons)以及设置各种系统属性。
2.挂载文件系统:根据.rc脚本的指令,init会创建并挂载系统运行所需的各个文件系统,如/dev、/proc、/sys,以及system、vendor、data等关键分区。
3.启动原生服务:init负责启动Android系统运行所需的各种原生后台服务,例如ueventd(设备事件管理)、logd(日志服务)、adbd(ADB调试服务)以及Zygote(应用进程孵化器)等。
4.属性管理:Android里大多数系统服务都是运行在system_service进程里的,属性服务除外,它在init里运行。
5.事件响应与进程管理:在系统正常运行期间,init会持续监控系统事件,例如设备插拔、服务退出等,并根据.rc脚本中的规则执行相应的操作,如重启异常退出的关键服务。
这里先看看RC脚本的格式,理解它对理解init进程至关重要!
rc 脚本格式与解析
.rc文件是Android Init Language(AIL)的核心,它定义了系统初始化的具体步骤。init进程在启动时会加载并解析多个.rc文件,最核心的是位于system/etc/init/目录下的init.rc。这些文件由五种主要类型的语句组成:Actions(动作)、Commands(命令)、Services(服务) 、 Options(选项) 和 Import(导入),下面分别介绍:
Import
Import就是导入其它rc文件的,init默认在/init.rc下加载rc文件,这个rc里就会通过import语句去加载其它位置的rc文件,init执行import是同步递归执行的,其格式与例子如下:
import <path> # path是rc文件或包含rc文件的目录的路径
# 如:
import /system/etc/init/ # 按文件名字母序导入当前目录下所有rc文件
import /system/etc/init/hw/init.rc # 直接导入单个文件
RC里存在依赖关系,而且后导入的会覆盖之前导入的,故导入顺序至关重要
Actions
Action是一系列命令的集合,由一个触发器(Trigger)来启动,触发器是一个字符串,代表某个特定事件。常见的触发器有:
early-init类:init进程启动到某一阶段时按固定顺序触发。property:<name>=<value>类: 当某个系统属性被设置为特定值时触发。dynamic event类:它们在系统运行期间都可能被动态触发,如shutdown/class_start/service_crashed等custom event类:开发者可以在rc中定义很多自定义事件,再通过triger <custom event>触发
当某个事件发生时,满足触发条件的Action就会被执行,格式如下:
on <trigger> [&& <trigger>]*
<command>
<command>
...
# 如:
# /system/etc/init/hw/init.rc
on boot
# 在系统启动时执行一系列挂载操作
mount_all --early
on property:sys.boot_completed=1
# 当系统完全启动后,执行一些清理工作
rm /data/nativetest/nativetest.data
在Init里硬编码了一些事件(触发器),有必要理解他们,开发者也可以在这些事件之间自己新增事件,下面按顺序说明固有的:
1.early-init:用于准备最小运行环境
2.init:开始挂载核心分区(/system /vendor等只读分区),加载SELinux策略等
3.post-fs:开始执行依赖核心分区的初始化,如启动logd服务等
4.post-fs-data:/data分区挂载并解密后,创建/data下子目录,启动依赖/data的服务
5.boot:此时系统核心服务都启动了,它开始启动大多数应用层和硬件相关的服务,设置sys.boot_completed=1属性
6.late-init:执行一些非常靠后、优先级不高的任务,或进行一些最终的配置
Commands
Command是在Action中被执行的具体操作。init支持丰富的命令集,以下是一些常用命令:
exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]*: 执行一个指定的程序start <service>: 启动一个已定义的服务stop <service>: 停止一个正在运行的服务setprop <name> <value>: 设置一个系统属性mkdir <path> [mode] [owner] [group]: 创建一个目录mount <type> <device> <path> [ <mountoption> ]*: 挂载一个文件系统write <path> <content>: 向一个文件写入内容
Services
Service定义了一个需要由init启动和管理的原生程序(后台守护进程),格式如下:
service <name> <pathname> [ <argument> ]* # 服务的唯一名称 可执行文件的路径 启动参数
<option>
<option>
...
关于服务的启动方式与样例等,见下面options节
Options
Option用于修饰一个Service,定义其运行方式和权限,含:
class <name>: 为服务指定一个类别。init会按固定顺序同时启动或停止同一类别的所有服务,常见的类别有core、main、late_startuser <username>: 指定服务运行的用户名,默认为rootgroup <groupname> [ <groupname> ]*: 指定服务运行的用户组seclabel <seclabel>: 指定服务的SELinux安全上下文critical: 标记为关键服务。如果该服务在4分钟内退出超过4次,系统将重启并进入Recovery模式oneshot: 服务启动后,init不会主动监控它,即使退出也不会重启disabled: 服务默认不启动,需要通过start <service>命令显式启动socket:init为服务创建一个unix domain socket,此时服务可能未启动(disabled),但若有对socket的连接,init就会启动它
例如:
# /system/core/rootdir/init.rc
# 定义logd服务,用于系统日志记录
service logd /system/bin/logd
class core
user logd
group logd system
seclabel u:r:logd:s0
# 定义adbd服务,用于ADB调试
service adbd /system/bin/adbd
class core
user shell
group shell adb
seclabel u:r:adbd:s0
disabled
socket adbd stream 660 shell shell
在这个例子中,adbd服务被定义为core类别,默认是disabled(禁用)状态。只有当ro.debuggable属性为1时,相关的Action才会被触发,通过start adbd命令来启动它。
Init源码分析
它从system/core/init/main.cpp的main函数开始:
int main(int argc, char** argv) {
if (IsFirstStage()) {// 是否为第一阶段init
return FirstStageMain(argc, argv);
}
InitLogging(argv); // 初始化日志系统
LOG(INFO) << "init second stage started!";
MountFirstStage(); // 创建并挂载核心的虚拟文件系统
property_init(); // 初始化属性服务 (property service)
int property_socket_fd = get_property_socket_fd();
epoll_add(epoll_fd, property_socket_fd); // 监听属性服务的写请求
LoadBootScripts(); // 加载并解析所有.rc脚本
ActionManager& am = ActionManager::GetInstance();
am.QueueEventTrigger("early-init");// 将启动事件按顺序加入队列
am.QueueEventTrigger("init");
am.QueueEventTrigger("late-init"); // 注:post-fs等是由mount_all命令间接触发的
while (true) {
ExecuteQueuedActions(); // 执行所有已到期的命令和待处理的事件 这会处理上面QueueEventTrigger放入的事件
auto next_timeout = GetNextRebootTimeout(); // 计算下一次超时时间
int epoll_events = epoll_wait(epoll_fd, ... , next_timeout); // 使用epoll等待事件发生,或直到超时
if (epoll_events > 0) { // 处理发生的事件
ProcessEpollEvents(); // 处理如属性变化、socket连接等
}
ReapAnyZombieChildren();// 重启崩溃的关键服务
}
return 0;
}
这里面有两个重要的点,一个是rc文件解析,它会在LoadBootScripts创建一个ActionParser实例,后者递归解析,创建遇到的Action和Service对象,将其注册到对应的管理器中,解析完毕整个启动蓝图就完成了,它就知道在什么时候执行什么操作了。而第二个点是属性服务的初始化,属性服务十分重要,高版本和低版本结构也发生了重大变化,它在system/core/init/property_service.cpp中实现,大体上长这样:
void property_init() {
// 创建共享内存区域
// 这块内存被映射到一个全局都知道的符号 "__system_property_area__"
// 使得libc库可以轻易地找到并附加到它上面。
prop_area* pa = __system_property_area_init__(); // 这个函数负责分配一块内存,并将其组织成一个特定的结构,prop_area
load_properties_from_file("/system/etc/prop.default"); // 从多个位置加载属性文件
load_properties_from_file("/odm/etc/prop.default");
load_properties_from_file("/vendor/etc/prop.default");
// ... 最后是 build.prop
load_build_prop(); // 加载 /system/build.prop, /vendor/build.prop 等
const int s = CreateSocket(kPropertySocketName, SOCK_STREAM, ...); // 创建并监听用于接收 setprop 请求的 socket
listen(s, 8);
property_set_socket_fd = s;
}
Zygote 进程
Zygote(孵化器)是Android系统中(几乎)所有Java进程的父进程,它的启动标志着系统从原生层进入Java框架层。它由init进程启动,其rc通常在init.zygote32.rc或init.zygote64.rc中,这分别启动32和64位版本的app_process:
# system/core/rootdir/init.rc (或相关 .rc 文件)
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server # 定义 zygote 服务 指定其二进制文件和启动参数
class main # 归入`main`服务类,确保它在系统核心服务(`core`类)启动后,在主要应用服务阶段被启动
priority -20
user root
group root readproc
socket zygote stream 660 root system # 创建一个名为zygote的socket,用于与AMS通信,只有root和system组有权访问它
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
当init执行上述命令后,/system/bin/app_process64开始运行。这是一个C++程序,其入口在frameworks/base/cmds/app_process/app_main.cpp。
// frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]) {
// ... 解析命令行参数 ...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); // 创建`AppRuntime`对象,它是`AndroidRuntime`的子类。这个类是Android原生框架与Java虚拟机(ART)交互的桥梁。
// ... 将参数(如 --zygote)设置给 runtime ...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote); // 正常流程
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote); // 不走孵化器启动apk
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
return 1;
}
}
// frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
// ...
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
// ...
}
这是从原生层到Java层的核心跳转。start方法内部会执行以下关键操作:
- 启动ART虚拟机:调用JNI_CreateJavaVM创建并初始化一个ART虚拟机实例
- 注册JNI方法:将frameworks/base/core/jni/目录下的C++函数注册为Java的native方法,供Java层调用
- 寻找入口类:在虚拟机中查找名为com.android.internal.os.ZygoteInit的Java类
- 调用main方法:找到ZygoteInit类中的public static void main(String[] argv)方法,并调用它
至此,控制权从C++正式转给Java。现在com.android.internal.os.ZygoteInit类的main方法开始执行,这是Zygote的Java心脏:
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
//注册Zygote服务端Socket
registerZygoteSocket(socketName); // 它会创建一个`ZygoteServer`实例,并通过`LocalServerSocket`类来监听之前由`init`创建的、名为`zygote`的socket文件描述符。从此,Zygote准备好接收来自AMS的命令
// 预加载核心类和资源
preload();
// 启动SystemServer进程
if (startSystemServer) {
forkSystemServer(abiList, socketName); // 最终会调用`SystemServer.main()`,开始启动AMS、WMS等所有核心服务
}
// 进入无限循环,等待AMS的连接请求来创建应用进程
runSelectLoop(abiList);
}
static void preload() {
// 为了加快应用的启动速度,Zygote会预加载Android框架中的核心类库(如`framework.jar`)和系统资源(如`framework-res.apk`)到内存中。这些资源是只读的,并能在所有应用进程之间共享,极大地减少了内存占用和应用启动时间。
preloadClasses(); // 读取一个名为preloaded-classes的文本文件,并使用Class.forName()将它们一一加载到VM中
Resources.preloadResources(); // 加载系统默认主题的资源,包括drawable、layout等,通常打包在framework-res.apk中
nativePreloadAppProcessHALs(); // 预加载应用进程可能需要的硬件抽象层(HALs)
maybePreloadGraphicsDriver(); // 根据系统属性,选择性地预加载图形驱动程序
preloadSharedLibraries(); // 预加载一些系统级的共享库
preloadTextResources(); // 预加载文本资源,可能包括字体或本地化文本
HttpEngine.preload(); // 调用网络引擎的preload方法
WebViewFactory.prepareWebViewInZygote(); // 请求WebViewFactory执行任何必须在Zygote进程中完成的初始化,以实现内存共享,这对所有使用WebView的应用都至关重要
warmUpJcaProviders();// “预热”JCA(Java加密体系结构)提供者,以减少首次加密操作时的延迟
}
SystemServer 进程
SystemServer是Android框架层的核心,它由Zygote进程fork而来,负责启动和管理系统中几乎所有的核心服务。该进程运行在system_server的SELinux上下文中,拥有较高的权限。SystemServer的执行入口是SystemServer.java的main方法,它首先会进行一些基本的初始化,如设置时区、加载Android的JNI库等:
// frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
// ...
createSystemContext(); // 创建系统上下文
mMainLooper = Looper.getMainLooper(); // 创建主循环
startBootstrapServices(); // 引导服务是最核心的服务,含ActivityManagerService/PowerManagerService/PackageManagerService等
startCoreServices(); // 启动核心服务,如WindowManagerService/DisplayManagerService等
startOtherServices(); // 启动其它服务,如ConnectivityService/NotificationManagerService等
startApexServices(t); //
// ...
}
当所有服务都成功启动后,SystemServer中的ActivityManagerService会执行最后一个步骤,启动一个CATEGORY_HOME类型的Intent,从而拉起桌面应用(Launcher),当Launcher应用启动并成功绘制出桌面UI时,整个Android系统的启动过程便宣告完成,设备进入可交互状态
Launcher 启动
Launcher本质上是一个系统应用,是用户与Android系统交互的主界面。它的启动标志着整个系统引导流程的结束。
-
启动请求:
SystemServer中的ActivityManagerService(AMS) 在确认所有关键服务都已就绪后,会创建一个ACTION_MAIN和CATEGORY_HOME的Intent,并通过startActivity请求启动桌面应用。 -
进程创建:AMS收到请求后,会通过Socket向Zygote进程发送一个启动新进程的请求。Zygote接收到请求后,
fork自身创建一个新的进程,该进程就是Launcher应用的宿主进程。 -
应用初始化:Launcher进程启动后,会执行
ActivityThread的main方法,创建主线程Looper和Application对象,并调用Application.onCreate()。 -
Activity创建与UI绘制:接着,Launcher进程会创建主Activity(通常是
com.android.launcher3.Launcher),并依次调用其onCreate()、onStart()、onResume()生命周期方法。在这些方法中,Launcher会加载布局文件、读取已安装的应用列表(通过PackageManager)、并将应用图标、小部件等UI元素绘制到屏幕上。这个过程由WindowManagerService(WMS) 协调完成。 -
显示第一帧:当Launcher的UI成功渲染并显示在屏幕上时,用户就可以开始与设备进行交互了。此时,系统会打印一条关键日志,标志着启动完成,例如:
ActivityManager: Displayed com.android.launcher3/.Launcher: +3s534ms这个时间表示从启动Intent到第一帧绘制完成所花费的时间。至此,Android的整个启动流程宣告结束。
APP启动
Launcher阶段
启动APP的方式有很多,这里以launcher里点击图标为例分析:
1.触摸屏驱动捕获到点击,将事件传递给窗口管理器(WindowManagerService, WMS),WMS判断出这个点击落在了桌面(Launcher)的某个App图标上。
2.Launcher收到点击事件后,判断出本次点击意图是启动另一个App。于是,它会创建一个Intent对象,它里面写着要启动的目标,如:
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("com.example.app", "com.example.app.MainActivity"));
startActivity(intent);
3.Launcher调用startActivity(),这个请求通过Binder IPC发送给ActivityManagerService(AMS)。
AMS阶段
AMS是安卓四大组件的管理者,它负责启动和管理个组件,当收到Launcher(其实才不管是谁的)的请求后,它开始如下处理:
1.解析Intent后向PackageManagerService(PMS)查询目标组件的相关信息
2.PMS把App的详细信息(ActivityInfo等)返回给AMS,AMS会检查这个App的进程是否已经存在,若已存在则直接通知进程创建并启动新的Activity实例;而不存在则准备创建新进程
3.若要创建新进程,AMS会收集好所有必要的信息,如App的用户ID(UID)、所需的权限、SELinux的安全上下文、调试标志位等,将其通过上面提到的Socket发送给孵化器
Zygote阶段
它分为prefork,fork,postfork三阶段。fork自不必多说,需注意的是它不是直接fork,而是先做了些预处理(prefork阶段),在这个阶段它会停止4个线程服务等,而中间是postfork阶段,它叫Specialize,它主要是做如下的事:
1.设置UID、GID和权限,把它关进自己的安全沙箱里。
2.关闭和父进程共享的资源,比如Zygote用来监听请求的Socket。
3.启动JNI,并为这个新进程设置一个友好的进程名(比如com.example.app)。
App初始化阶段
经过特化后,这个新进程终于摆脱了Zygote的影子,准备好运行App自己的代码了。
1.入口:ActivityThread:进程的控制权被交给android.app.ActivityThread类的main方法。可以把它看作是App世界的真正入口。
// ActivityThread.java
public static void main(String[] args) {
// ...
Looper.prepareMainLooper(); // 准备主线程的消息循环
ActivityThread thread = new ActivityThread();
// 关键一步:让新进程向AMS“报到”,建立双向通信
thread.attach(false, startSeq);
Looper.loop(); // 启动消息循环,从此主线程开始等待和处理消息
// ...
}
2.向AMS报到 (attach):在attach方法里,新进程会通过Binder主动连接AMS,告诉它:“我准备好了!”。AMS收到报到后,才算真正纳管了这个新进程。
3.生命周期的开始:AMS确认新进程就绪后,会通过Binder反向调用,命令它开始执行真正的Activity生命周期。首先,创建并调用Application.onCreate()。这是App全局初始化的地方,适合放一些只需要执行一次的设置。然后,创建目标Activity(如MainActivity)的实例,并依次调用它的onCreate()和onResume()方法。
4.绘制UI:在Activity的onCreate()中,开发者通常会调用setContentView()来加载布局文件。这个调用会触发View的测量(measure)、布局(layout)、绘制(draw)三大流程。最终,由WindowManagerService负责将这个App的窗口合成到屏幕上。
当第一帧画面成功渲染出来,整个启动流程才算真正完成。这时,你终于看到了App的界面,可以开始与它交互了。
关于这部分,详请查阅Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]
ADB
安卓调试桥由三部分组成:
1.adb-client:运行在PC,提交的命令交由adbs转发给adbd
2.adb-server(adbs):运行在PC,连接client和daemon,支持usb/tcp连接
3.adb-daemon(adbd):运行在Android内部,连接adbs
`shell su -c am start -n com.tencent.mm/com.tencent.mm.plugin.webview.ui.tools.WebViewUI -a android.intent.action.VIEW -d https://baidu.com`
adb shell dumpsys activity top|grep ACTIVITY # 最顶层
adb shell dumpsys activity activities | grep mResumedActivity # 当前activity
adb tcpip 5555 # 重启设备的adbd并将其绑定到5555端口,若存在多个设备可使用-s serial指定,下文同
adb shell ip addr show wlan0 # 查看ip地址
adb connect ip-address-of-device:5555 # 让adbs连接到adbd
adb devices # 验证
adb disconnect <device-ip-address> # 断开连接
参考
[1] Penetrating the World of Android: MacBook M1
[3] ab分区
[4] CF
[5] linegaeos: Building for Emulator/AVD
[6] ddk