3、SystemServer入口函数:main()方法的调用栈、参数解析、系统属性初始化
好,咱们直接进入正题。
SystemServer 的入口,就是那个经典的 main() 方法。你想想看,整个 Android 系统服务的大管家,就是从这一行代码开始的。我当年第一次看这块代码时,心里就一个感觉:这入口怎么这么「干净」?
3.1 main() 方法的调用栈
先别急着看代码,咱们捋一下调用链。
SystemServer 的 main() 是被 Zygote 进程 fork 出来之后,通过反射调用的。具体来说,ZygoteInit 在 fork 出子进程后,会调用 RuntimeInit.zygoteInit(),然后一路走到 SystemServer.main()。
我画个简化的调用栈给你看:
ZygoteInit.main()
└─> ZygoteInit.zygoteInit()
└─> RuntimeInit.zygoteInit()
└─> RuntimeInit.invokeStaticMain()
└─> SystemServer.main() // 反射调用
└─> SystemServer.run()
嗯,这里要注意一点:invokeStaticMain() 是通过反射找到 SystemServer 类的 main() 方法并执行的。为什么用反射?说白了,就是为了解耦。Zygote 不需要在编译时依赖 SystemServer,运行时动态加载就行。
3.2 参数解析
main() 方法接收一个 String[] args 参数。你可能会问:SystemServer 启动时传了什么参数?
其实,在正常的 Android 启动流程中,main() 的参数通常是空的。但 Google 留了个后门——你可以通过系统属性 sys.system_server.extra_args 来传递参数。
看代码:
public static void main(String[] args) {
// 解析参数
if (args != null && args.length > 0) {
// 处理参数
}
// 关键:从系统属性读取额外参数
String extraArgs = SystemProperties.get("sys.system_server.extra_args");
if (extraArgs != null && !extraArgs.isEmpty()) {
// 合并参数
}
new SystemServer().run();
}
我个人习惯在调试时用这个属性。比如你想让 SystemServer 启动后直接进入某个特定模式,就可以:
adb shell setprop sys.system_server.extra_args "--debug --profile"
然后重启系统服务。这个技巧在调试启动性能时特别有用。
3.3 系统属性初始化
接下来是重头戏——系统属性初始化。
SystemServer 在 run() 方法里,第一件事就是初始化系统属性。为什么?因为后面所有服务都要依赖系统属性来获取配置信息。
看这段代码:
private void run() {
// 1. 设置时区
TimeZone.setDefault(null);
// 2. 设置语言环境
Locale.setDefault(Locale.US);
// 3. 初始化系统属性
SystemProperties.set("sys.system_server.starting", "true");
// 4. 加载库
System.loadLibrary("android_servers");
// ... 后续服务启动
}
这里有个细节:TimeZone.setDefault(null) 是什么意思?
说白了,就是让系统从系统属性 persist.sys.timezone 中读取时区设置。如果传 null,系统会自动去查属性。我刚开始做这块时,以为传 null 会报错,结果发现这是 Android 的「潜规则」。
| 系统属性 | 作用 | 初始化时机 |
|---|---|---|
| sys.system_server.starting | 标记 SystemServer 正在启动 | run() 方法开头 |
| persist.sys.timezone | 系统时区 | TimeZone.setDefault(null) 时读取 |
| sys.boot_completed | 标记启动完成 | 所有服务启动后设置 |
还有一个容易被忽略的点:System.loadLibrary("android_servers")。这个 native 库包含了 SystemServer 的 JNI 方法。如果加载失败,整个 SystemServer 会直接崩溃。
嗯,说到崩溃,我记得有一次在移植系统时,发现 SystemServer 启动到一半就挂了。查 log 发现是 libandroid_servers.so 的依赖库没找到。所以如果你做系统移植,一定要检查这个库的依赖链。
3.4 小结
咱们总结一下这一节的核心内容:
- 调用栈: Zygote → RuntimeInit → SystemServer.main() → run()
- 参数解析: 支持通过系统属性传递额外参数,方便调试
- 属性初始化: 时区、语言、启动标记等,顺序不能乱
下一节,咱们会深入 run() 方法,看看 SystemServer 是如何一步步创建各种系统服务的。你想想看,从一行 main() 到上百个服务同时运行,这中间到底发生了什么?
到时候见。