前言
安卓启动流程
1. 启动电源,加载引导程序
接通电源并启动时,引导芯片代码从预定义的地方(固化在ROM)开始执行,加载引导程序 BootLoader 到RAM中。
2.执行引导程序BootLoader
Android系统运行前会先运行 BootLoader,它的作用是拉起并运行系统OS。这个类似与 PC 的 bios,作用是初始化硬件和驱动为最终调用系统内核做好准备。
3.启动Linux内核
在内核的启动过程中,会设置缓存、加载驱动等。当内核完成系统设置后,它会在系统文件中寻找 init.rc 文件,解析并启动 init 进程。
Init 进程
基于 Android 12 源码
从 Android 10 开始 ,Google 对 init
进程进行了重构,将其拆分为 两个阶段:
阶段 | 名称 | 职责 | 运行时机 |
---|---|---|---|
第一阶段 | First Stage Init | 最基础的初始化:挂载 /dev 、/proc 、/sys ,准备 selinux ,启动 ueventd |
内核刚启动,根文件系统为 ramdisk |
第二阶段 | Second Stage Init | 解析 init.rc 、启动 zygote 、servicemanager 等核心服务 |
第一阶段完成后,通过 execv 跳转 |
在安卓 10 之前,入口函数 int main(int argc, char** argv)
在 /system/core/init/Init.cpp 中,在安卓 12 中main()
函数位于:/system/core/init/main.cpp
。
Android 12 把早期引导拆成三个阶段:① first-stage init → ② SELinux setup → ③ second-stage init。
可以在 system/core/init/README.md
中查看分层介绍
/system/core/init/main.cpp:
scss
int main(int argc, char** argv) {
// 调试能力,便于在开发/测试阶段发现底层内存问题
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
// Boost prio which will be restored later
// 设置当前进程(PRIO_PROCESS)的调度优先级为 -20(Linux 中优先级范围是 -20 到 19,-20 最高)
// 启动时临时提升优先级,后续再进行降级(通常是 0)
setpriority(PRIO_PROCESS, 0, -20);
// 判断程序是否以 ueventd 名字被调用
// 如果 init 被软链接为 ueventd(如 /sbin/ueventd),则进入 ueventd_main(),作为 ueventd 运行。
// 否则继续执行 init 主流程。
// !strcmp(argv[0], "ueventd") 这种写法就等同于 判断两个字符串是否相同。
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
// argc > 1 表示至少有一个参数(argv[0] 是程序名,argv[1] 是第一个参数)
// init 的行为由参数决定,这是实现"多阶段启动"的关键。
if (argc > 1) {
// subcontext 子上下文
// 相当于初始化程序运行时必要的环境,通常用于运行脚本或服务前的环境准备
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// selinux_setup 是第一阶段中专门用于 加载和设置 SELinux 策略 的子阶段
// selinux:强制访问控制(MAC)安全模块,安全看门人,安卓保持安全的关键!
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// 第二阶段主流程
// 这是 Android 系统"真正启动"的标志,Zygote 启动后,App 才能被创建。
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// 第一阶段,如果没有参数,或参数不匹配上述任何一种,默认进入第一阶段。
return FirstStageMain(argc, argv);
}
- first-stage init:最小化环境下完成"能把系统盘挂起来"的工作(含 dm-verity/AVB、动态分区、早期内核模块等)。
- SELinux setup:组合 platform/vendor 的 sepolicy,切到 enforcing,再继续。
- second-stage init :解析并执行
init
语言(actions/commands/services/options/imports)、启动属性服务与各类系统守护、装载 APEX 后的脚本、拉起 Zygote 等。
一句话概况就是:挂盘→上策略→跑业务。
如果不深入去看,就会发现 main 函数只执行了一次,也就是说 SetupSelinux,SecondStageMain,FirstStageMain 三个函数只会执行一个啊,那么为什么说它们是按顺序执行的?
其实不然,main()方法会被执行三次:
第一次进入 main()
→ 进入 FirstStageMain()
内核启动后,直接执行 /init
。此时 argc == 1
,argv[1]
不存在(或为 null)。所以 if (argc > 1)
条件不成立,直接跳到最后一行。
在 /system/core/init/first_stage_init.cpp 中,有FirstStageMain()
的实现,在其最后一行中有这么几行代码:
ini
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
execv(path, const_cast<char**>(args));
execv
会用新的程序替换当前进程的镜像 ,但保持 PID 不变(还是 1)。- 这里它又执行
/init
,但传入了参数"selinux_setup"
。
第二次进入 main()
→ 进入 SetupSelinux()
在 /system/core/init/selinux.cpp 中 的SetupSelinux()
最后同样包含了类似信息:
arduino
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
再次 execv
,传入 "second_stage"
参数。
第三次进入 main()
→ 进入 SecondStageMain()
开始解析 init.rc
、启动 zygote
等。
流程大致如下:
scss
内核启动
↓
执行 /init(main 函数入口)
↓
main() → 默认进入 FirstStageMain() ← 注意:这是第一次进入 main
↓
FirstStageMain() 完成基础初始化
↓
execv("/init", "selinux_setup", ...) ← 重新调用自己,传入 "selinux_setup"
↓
main() → 因为 argv[1] == "selinux_setup",进入 SetupSelinux()
↓
SetupSelinux() 加载 SELinux 策略
↓
execv("/init", "second_stage", ...) ← 再次重新调用自己,传入 "second_stage"
↓
main() → 因为 argv[1] == "second_stage",进入 SecondStageMain()
↓
SecondStageMain() 启动 zygote、servicemanager 等核心服务
↓
系统启动完成
说了一大堆,其实才到关键代码的入口,Android 系统启动的核心,Android 用户空间真正"活起来"的起点。
SecondStageMain(int argc, char** argv)
位于:system/core/init/init.cpp
源码:
c++
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
// 初始化内核日志
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
// Update $PATH in the case the second stage init is newer than first stage init, where it is
// first set.
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
}
// Init should not crash because of a dependence on any other process, therefore we ignore
// SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
// is inherited across exec, but custom signal handlers are not. Since we do not want to
// ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
{
struct sigaction action = {.sa_flags = SA_RESTART};
action.sa_handler = [](int) {};
sigaction(SIGPIPE, &action, nullptr);
}
// Set init and its forked children's oom_adj.
// 这行代码将 init 进程(pid=1)的 /proc/1/oom_score_adj 文件写入一个预设的值
// DEFAULT_OOM_SCORE_ADJUST。oom_score_adj 用于调整进程在系统内存不足
//(OOM, Out of Memory)时被杀死的优先级。这里的目的是降低 init 进程被杀死的可能性,
// 确保系统核心进程的稳定。
if (auto result =
WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
!result.ok()) {
LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
<< " to /proc/1/oom_score_adj: " << result.error();
}
// Set up a session keyring that all processes will have access to. It
// will hold things like FBE encryption keys. No process should override
// its session keyring.
// 创建会话密钥环
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
// Indicate that booting is in progress to background fw loaders, etc.
// 标记系统正在启动中。
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// See if need to load debug props to allow adb root, when the device is unlocked.
const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
bool load_debug_prop = false;
if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
load_debug_prop = "true"s == force_debuggable_env;
}
unsetenv("INIT_FORCE_DEBUGGABLE");
// Umount the debug ramdisk so property service doesn't read .prop files from there, when it
// is not meant to.
if (!load_debug_prop) {
UmountDebugRamdisk();
}
// PropertyInit() 负责初始化属性服务所需的内部数据结构。
// StartPropertyService 则真正启动这个服务。
// 属性服务是 Android 中一个非常重要的 IPC(进程间通信)机制,其他进程可以通过读取这些
// 属性来判断系统的状态。
PropertyInit();
// 此时开始可以读写系统属性(如 ro.boot.*)
// Umount second stage resources after property service has read the .prop files.
UmountSecondStageRes();
// Umount the debug ramdisk after property service has read the .prop files when it means to.
if (load_debug_prop) {
UmountDebugRamdisk();
}
// Mount extra filesystems required during second stage init
MountExtraFilesystems();
// Now set up SELinux for second stage.
// SELinux 上下文恢复
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
PLOG(FATAL) << result.error();
}
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
StartPropertyService(&property_fd);
// Make the time that init stages started available for bootstat to log.
RecordStageBoottimes(start_time);
// Set libavb version for Framework-only OTA match in Treble build.
if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
SetProperty("ro.boot.avb_version", avb_version);
}
unsetenv("INIT_AVB_VERSION");
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
MountHandler mount_handler(&epoll);
SetUsbController();
SetKernelVersion();
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
if (!SetupMountNamespaces()) {
PLOG(FATAL) << "SetupMountNamespaces failed";
}
InitializeSubcontext();
// 这部分是 init 脚本执行和系统服务启动的引擎。
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
// LoadBootScripts 函数会解析 /init.rc 以及其他相关的 .rc 文件。
// 这些文件定义了 Android 启动时需要执行的 actions (动作) 和 services (服务)。
// ActionManager 和 ServiceList 则分别是管理这些动作和服务实例的单例。
LoadBootScripts(am, sm);
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
// Make the GSI status available before scripts start running.
auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";
SetProperty(gsi::kGsiBootedProp, is_running);
auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
SetProperty(gsi::kGsiInstalledProp, is_installed);
// 队列化内置动作和事件触发
// QueueBuiltinAction 将一些硬编码在 init 源码中的内置动作添加到待执行队列中
// 例如 SetupCgroupsAction 用于设置进程控制组
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
Keychords keychords;
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
for (const auto& svc : ServiceList::GetInstance()) {
keychords.Register(svc->keycodes());
}
keychords.Start(&epoll, HandleKeychord);
return {};
},
"KeychordInit");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
// Restore prio before main loop
setpriority(PRIO_PROCESS, 0, 0);
// 主循环
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!IsShuttingDown()) {
auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
auto pending_functions = epoll.Wait(epoll_timeout);
if (!pending_functions.ok()) {
LOG(ERROR) << pending_functions.error();
} else if (!pending_functions->empty()) {
// We always reap children before responding to the other pending functions. This is to
// prevent a race where other daemons see that a service has exited and ask init to
// start it again via ctl.start before init has reaped it.
ReapAnyOutstandingChildren();
for (const auto& function : *pending_functions) {
(*function)();
}
}
if (!IsShuttingDown()) {
HandleControlMessages();
SetUsbController();
}
}
return 0;
}
关键代码解析:
主循环
c++
while (true) {
// ...
am.ExecuteOneCommand();
// ...
auto pending_functions = epoll.Wait(epoll_timeout);
// ...
}
while (true)
循环是 init
的核心。am.ExecuteOneCommand()
每次从动作队列中取出一个命令并执行。epoll.Wait()
是一个高效的I/O多路复用函数,它让 init
进程在没有事件发生时进入休眠状态,以节省 CPU 资源。当有子进程退出、属性值改变或收到控制消息时,epoll
会被唤醒,init
进程随即处理这些事件。这个循环会一直运行,直到系统关机。
init.rc
查看 LoadBootScripts 方法可知
c++
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
// 如果没有指定自定义 init_rc,则加载默认路径
parser.ParseConfig("/system/etc/init/hw/init.rc"); // ← 真正的主配置
parser.ParseConfig("/system/etc/init"); // 加载 system 服务
parser.ParseConfig("/system_ext/etc/init"); // system_ext
parser.ParseConfig("/vendor/etc/init"); // 厂商服务
parser.ParseConfig("/odm/etc/init"); // ODM 服务
parser.ParseConfig("/product/etc/init"); // 产品服务
} else {
// 如果指定了 ro.boot.init_rc,则加载它(用于 recovery 等特殊模式)
parser.ParseConfig(bootscript);
}
}
默认解析的 是/system/etc/init/hw/init.rc 而不是其他文章所介绍的 位于 /system/core/rootdir/init.rc(不过这个文件仍存在),这是因为Android 8 开始,Google 把 system/vendor 分离 ,而且为了支持不同硬件,不再把 init.rc 固定在 ramdisk ,而是放到 /system/etc/init/hw/
,并且支持 import
进 vendor/product/odm/system_ext 的脚本。
system/core/rootdir/init.rc
,不再直接被运行,而是通过 build system 安装到 system 分区 ,运行时路径就是 /system/etc/init/hw/init.rc
,这里不做过多介绍。
在 init.rc 中 有 import 导入、on 命令和 service 服务,其中:
import
:模块化加载 .rc
文件,用于导入其他 .rc
配置文件,实现配置的模块化和分层管理。
c++
# 导入硬件相关的配置(如高通、MTK)
import /init.${ro.hardware}.rc
# 导入 USB 相关配置
import /init.usb.rc
# 导入 zygote 启动配置(根据设备类型)
import /init.${ro.zygote}.rc # 如 init.zygote64_32.rc
on
:事件触发器(Actions),on
定义动作(Action) ,当某个条件满足时,执行一系列命令。
c++
on early-init
# 设置关键目录
mkdir /dev/socket 0755
on init
# 挂载 tmpfs
mount tmpfs tmpfs /dev tmpfs mode=0755
on late-init
# 触发后续阶段
trigger early-fs
trigger fs
trigger post-fs
trigger late-fs
trigger post-fs-data
trigger boot
on property:vold.decrypt=trigger_restart_framework
# 当加密分区解密完成,重启 framework
class_start main
class_start late_start
on property:sys.boot_completed=1
# 系统启动完成,启动 bootanim(开机动画结束)
stop bootanim
service
:定义长期运行的守护进程,service
用于声明一个系统服务 ,init
进程会负责启动、监控和重启它。
c++
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
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
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
使用 CreateParser 创建一个 Parser,通过解析.rc 文件中的指令 name,来解析具体的命令。
c++
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(
&service_list, GetSubcontext(), std::nullopt));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
}
关于 init.rc 更详细的介绍可以看看这篇文章:安卓11 init初始化以及init.rc的解析执行过程详解
解析器关键代码,前面的文件夹判断及其他条件判断省略:
c++
void Parser::ParseData(const std::string& filename, std::string* data) {
// 在数据末尾添加换行符和空终止符,确保解析器能正确识别最后一行
data->push_back('\n');
data->push_back('\0');
// 解析状态结构体,记录当前解析位置、行号、下一个 token 类型
parse_state state;
state.line = 0;
state.ptr = data->data(); // 指向数据起始位置
state.nexttoken = 0;
SectionParser* section_parser = nullptr; // 当前正在处理的"段"解析器(如 service、on)
int section_start_line = -1; // 当前段的起始行号(用于报错)
std::vector<std::string> args; // 存储当前行拆分出的 tokens(命令和参数)
// 标记是否遇到了无效的段声明,用于跳过错误直到下一个有效段
bool bad_section_found = false;
// Lambda:结束当前段的处理
auto end_section = [&] {
bad_section_found = false;
if (section_parser == nullptr) return;
// 通知当前段解析器:本段结束,进行收尾工作(如提交 service 配置)
if (auto result = section_parser->EndSection(); !result.ok()) {
parse_error_count_++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
section_parser = nullptr;
section_start_line = -1;
};
// 主循环:逐 token 解析文件
for (;;) {
switch (next_token(&state)) {
case T_EOF:
// 文件结束:结束当前段,并通知所有 SectionParser 文件解析完成
end_section();
// 通知所有注册的 SectionParser:整个文件已解析完毕
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
return;
case T_NEWLINE:
state.line++; // 行号递增
if (args.empty()) break; // 空行,跳过
// 1. 处理特殊行前缀(如 /sys/、/dev/),用于 uevent 规则(非主流用法)
auto line_callback = std::find_if(
line_callbacks_.begin(), line_callbacks_.end(),
[&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
if (line_callback != line_callbacks_.end()) {
end_section(); // 先结束可能的旧段
// 执行行回调(如处理设备节点规则)
if (auto result = line_callback->second(std::move(args)); !result.ok()) {
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
// 2. 如果当前行是"段开始"(如 service、on、import)
// 检查是否在 section_parsers_ 注册表中
} else if (section_parsers_.count(args[0])) {
end_section(); // 结束上一个段(防止嵌套)
section_parser = section_parsers_[args[0]].get();
section_start_line = state.line;
// 调用该段解析器的 ParseSection(),处理段头(如 service zygote /system/bin/app_process...)
if (auto result = section_parser->ParseSection(std::move(args), filename, state.line);
!result.ok()) {
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
bad_section_found = true; // 标记错误段,后续行跳过直到新段
}
// 3. 如果当前处于某个段内部(如 on boot 的后续命令行)
} else if (section_parser) {
// 调用该段解析器的 ParseLineSection(),处理段内行(如 start、setprop 等命令)
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result.ok()) {
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
// 4. 既不是段开始,也不在段内,且之前没发现错误段 → 语法错误
} else if (!bad_section_found) {
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line
<< ": Invalid section keyword found";
}
args.clear(); // 清空当前行参数,准备下一行
break;
case T_TEXT:
// 读取到文本 token,加入当前行参数列表
args.emplace_back(state.text);
break;
}
}
}
再回到上面的 main()方法:
c++
int main(int argc, char** argv) {
// ... 初始化
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
// 1. 解析 .rc 文件 → 填充 am 和 sm
LoadBootScripts(am, sm);
// 2. 进入主循环,开始执行
while (true) {
// 执行可运行的 Actions
am.ExecuteOneCommand();
// 处理其他事件(epoll、属性、子进程等)
epoll.Wait();
}
}
init.rc
→ Parser
→ ActionManager/ServiceList
→ Event Trigger
→ ExecuteOneCommand
→ fork/exec
→ 服务启动
对比老的安卓系统,可以看出 google 在源码上做了大量优化,虽然之前的文章可能解释不了新的源码,但是它们的核心逻辑变化不大,新的代码只是兼容性和扩展性更强了。
说真的,写到现在我还是懵的,这篇文章就算对 安卓启动的初窥门径吧,先了解个大概。