app_process入口
app_process 是 native 入口,但它不直接创建 Zygote 或启动应用类。它先计算可改写的 argv 区域,识别哪些参数属于 ART、哪些属于 app_process,再由 AppRuntime 把控制权交给 AndroidRuntime::start()。这篇文章只讲这一层的参数和回调边界;Zygote 如何预加载、fork SystemServer 和运行 socket 循环留在 SS001 与后续文章。
1. AppRuntime状态
源码文件:frameworks/base/cmds/app_process/app_main.cpp
相关类型:AppRuntime
class AppRuntime : public AndroidRuntime {
public:
AppRuntime(char* argBlockStart, const size_t argBlockLength)
: AndroidRuntime(argBlockStart, argBlockLength), mClass(NULL) {}
void setClassNameAndArgs(const String8& className,
int argc, char* const* argv) {
mClassName = className;
for (int i = 0; i < argc; ++i) {
mArgs.add(String8(argv[i]));
}
}
String8 mClassName;
Vector<String8> mArgs;
jclass mClass;
};AppRuntime 继承 AndroidRuntime,新增三项状态:启动类名、传给该类 main 的参数、JNI 全局类引用。setClassNameAndArgs() 复制字符串,而不是保存原始 argv 指针;这是因为后面 setArgv0() 会覆盖整个参数块来设置进程名,原始参数内存不能再作为稳定存储。
2. argv可改写区域
源码文件:frameworks/base/cmds/app_process/app_main.cpp
相关函数:computeArgBlockSize()、AppRuntime 构造函数
static size_t computeArgBlockSize(int argc, char* const argv[]) {
// Linux exec 通常把 argv 放在连续参数区;app_process 用它改写 argv[0]。
uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);
uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);
end += strlen(argv[argc - 1]) + 1;
return end - start;
}
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));这个尺寸只用于进程名改写,不是 Java 参数总长度限制,也不是 Zygote socket 缓冲区大小。源码注释还指出普通应用由 Zygote fork 出来,命令行空间沿用 Zygote 的大小;如果只按当前短参数重新计算,后续设置较长 nice name 可能被截断。
3. 参数分层算法
源码文件:frameworks/base/cmds/app_process/app_main.cpp
main() 把参数分成两段:遇到第一个非 - 参数前,内容交给 VM;随后跳过未使用的 parent dir,再解析 app_process 内部选项。-cp/-classpath 是例外,它们虽然带一个后续非短横线参数,仍被视为 VM 选项。
const char* spaced_commands[] = { "-cp", "-classpath" };
bool known_command = false;
int i;
for (i = 0; i < argc; i++) {
if (known_command) {
runtime.addOption(strdup(argv[i]));
known_command = false;
continue;
}
for (const char* command : spaced_commands) {
if (strcmp(argv[i], command) == 0) known_command = true;
}
if (argv[i][0] != '-') break;
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip the VM separator.
break;
}
runtime.addOption(strdup(argv[i]));
}这段循环的消费者是 AndroidRuntime,不是 Zygote。-- 是 VM 参数结束标记;没有它时,第一个非选项参数也会结束 VM 参数阶段。若把 --start-system-server 当成 VM 选项,Zygote 将收不到启动 SystemServer 的内部标志。
4. 启动分支
源码文件:frameworks/base/cmds/app_process/app_main.cpp
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else if (strncmp(arg, "--", 2) != 0) {
className = arg;
break;
} else {
--i;
break;
}
}随后分支如下:
Zygote 分支没有 className,因此会创建 dalvik-cache 目录、读取 64/32 位 ABI 属性并把剩余参数交给 ZygoteInit.main()。应用/工具分支必须有 className,并把 application 或 tool 放入 args,供 RuntimeInit 判断启动模式。
5. Runtime回调
源码文件:frameworks/base/cmds/app_process/app_main.cpp
5.1 onVmCreated
void AppRuntime::onVmCreated(JNIEnv* env) {
if (mClassName.empty()) return; // Zygote 不在这里查启动类。
char* slashClassName = toSlashClassName(mClassName.c_str());
mClass = env->FindClass(slashClassName);
free(slashClassName);
mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
}只有非 Zygote 模式需要提前 FindClass。源码注释解释了原因:如果等到 onStarted() 从 boot class 的 native 方法中查找,JNI 的类加载器上下文可能看不到 CLASSPATH 中的普通工具类。查找失败会记录错误,随后由后续启动阶段暴露;这里不能把 mClass 非空当作应用已经运行。
5.2 启动回调
void AppRuntime::onStarted() {
sp<ProcessState> proc = ProcessState::self();
proc->startThreadPool();
AndroidRuntime* ar = AndroidRuntime::getRuntime();
ar->callMain(mClassName, mClass, mArgs);
IPCThreadState::self()->stopProcess();
hardware::IPCThreadState::self()->stopProcess();
}
void AppRuntime::onZygoteInit() {
sp<ProcessState> proc = ProcessState::self();
proc->startThreadPool();
}两条回调的消费者不同:应用/工具启动后调用目标类的 main,返回后停止 kernel Binder 和 hwbinder 进程状态;Zygote 只启动 Binder 线程池,不调用应用类 main。线程池启动成功也不等于目标服务已注册,它只是为后续 Binder 调用准备消费者。
6. 错误与结束路径
- ABI 属性缺失:Zygote 分支调用
LOG_ALWAYS_FATAL,尚未进入ZygoteInit.main()。 - 没有 className 且没有
--zygote:打印 usage 并 fatal,属于入口协议错误。 - Java class 查找失败:
onVmCreated()记录错误,后续callMain不能获得有效目标类。 - 应用 main 返回:
onStarted()停止 Binder/hwbinder 状态后结束进程;这不是 Zygote 的常驻路径。 - Zygote 退出:
onExit()关闭 kernel Binder 和 hwbinder 进程状态,再调用AndroidRuntime::onExit()。
7. 源码导航
命令输入是 Android 17 checkout 中的相对路径,输出用于定位参数分支、回调和错误文本;它们不会启动设备。
rg -n "computeArgBlockSize|class AppRuntime|onVmCreated|onStarted|onZygoteInit|onExit" \
frameworks/base/cmds/app_process/app_main.cpp
rg -n "--zygote|--start-system-server|--application|--nice-name|runtime.start" \
frameworks/base/cmds/app_process/app_main.cpp
rg -n "AndroidRuntime::start|callMain|setArgv0|startThreadPool" \
frameworks/base/cmds/app_process frameworks/base/core/cmds下一篇将进入 AndroidRuntime::start(),解释 VM 创建、JNI 注册与 CallStaticVoidMethod 如何把控制权交给 ZygoteInit.main();不会重复本文的参数扫描循环。
