APK,启动!
引导加载程序-内核启动-init进程启动-Zygote进程启动-System Server进程-启动框架和用户界面-应用程序启动
总体上,安卓的架构分布底层的内核空间以Linux Kernel为基础,上层的用户空间由Native系统库、虚拟机运行环境(ART)、框架层(Java API Framework)组成。两层空间由系统调用(Syscall)联通。

相比于静态去看安卓的架构,任何系统是实时运行,不断交互的,逐步分析,庖丁解牛,可以发现架构间各层次的关系。

Boot Rom是记录了引导启动的只读寄存器(Read Only Memory),按下电源键后,改变引导芯片开始从固化在ROM里的预设代码开始执行,然后加载引导程序到RAM
随后,来到Boot Loader(我们熟悉的bl锁说的就是这里)启动安卓系统前的引导程序。 Bootloader有时被锁定,以防止未经授权的系统修改
Hardware Abstraction Layer 其中有多个库模块(蓝牙wifi等),当java框架层要访问硬件就会用到这
其中涉及到了我们最想关注的安卓层面的加载流程,虚拟机的作用就不再多说,经过swapper进程在native层启动了Init进程(pid = 1,也就是安卓源码中的init.cpp)init进程是所有用户进程的鼻祖
为什么这么说,看他的作用
在此层次中,init
进程解析了init.rc
文件fork
生成了关键的Zygote
进程(翻译为受精卵,此进程也称为安卓的应用进程孵化器,是所有应用进程的父进程)对于此孵化进程,他会加载
Zygote孵化的第一个进程是System Server
,此进程负责启动与管理整个Java Framework
包含了ActivityManager,WindowManager,PackageManager,PowerManager等服务。
Media Server不是由Zygote进程孵化而来的,而是由init生成,他负责启动与管理C++ framework
包含AudioFlinger,Camera Service等服务。
Zygote进程孵化出的第一个app进程为Launcher,通俗来讲就是我们的桌面app;所有的app进程都是由zygote进程fork而来的
第一个系统调用是native与内核之间的联系,第二个JNI是java层与native层之间的联系
从内核启动的第一个进程init开始看,我们在相应的目录,也就是安卓源码/system/core/init/目录下的init.cpp文件

关注main函数,可以大致把init的加载流程分为一下几个部分
程序入口,判断调用uevent还是看门狗守护进程
第一阶段又分为以下几个部分
【最后步骤很关键,重新启动init的过程也讲加载的权限进一步降低,在整个第一阶段都是以权限极高的kernel执行,转化为init域后权限降低,更为安全,这个就是SELinux安全策略的作用】
第二阶段分为以下几个部分
至此章节开始的流程图初始化的Zygote和ServiceManager步骤就知道是在哪里实现的了
最后一部分是个主循环
Zygote的执行过程大致可以概括如下图,我们简单分析一下涉及到的源码和函数,蓝色加深的是涉及的三个源码

从上文我们知道了Zygote进程是在init.rc中创建的,他对应的可执行程序叫app_process,对应的源文件是app_main.cpp
较为关键的几个地方就是开始的AppRuntime初始化, 是后续启动 Android 平台服务或应用程序的核心接口。 他是AndroidRuntime的子类,通过调用会通过继承的方法调用AndroidRuntime中的方法
我们关注start中的函数,
此函数的体量过于庞大。主要的功能是设置了虚拟机的参数,利用了很多的parseRuntimeOption函数(对于不同的软硬件环境,函数的参数往往需要调整、优化,从而使系统达到最佳性能)
安卓6到10对源码进行了一些调整 但是基础功能并未做出太大变动,一下是gityuan师傅的安卓6.0的代码注释
这个函数层层套用,最终实现的功能就是启动一些native层的函数方法的注册
书接上文提到的AndroidRuntime.cpp的最后步骤,也就是
这里说的main就是ZygoteInit.main()
(我们在app_main.cpp的最后是start了com.android.internal.os.ZygoteInit
这个包,接下来就是去调用他的main)
在mian中是先开启了DDMS功能 然后为Zygote注册socket,preload函数预加载类和资源,启动SystemServer进程
最后用一张流程图概括Zygote的总体加载流程就很清楚了

我从始至终都坚信一点,那就是学校绝不会成为制约一个人成长的天花板,只要你肯学,只要你愿意努力,无论在哪,你就一定能有所作为。学校固然重要,但更重要的是自己的努力。为此,从研一便开始为自己的职业生涯做打算,并付之于行动。
学习力比知识更重要,因为知识可能落伍,但学习力能让你紧跟技术潮流,立于处不败之地。
Linux之父Linus Torvalds的一句名言:Read the fucking source code。
本文参考了很多Gityuan大师傅的博客文章,系统讲解了安卓的知识。
Android 操作系统架构开篇 - Gityuan博客 | 袁辉辉的技术博客
Android系统启动-zygote篇 - Gityuan博客 | 袁辉辉的技术博客
Android系统启动-Init篇 - Gityuan博客 | 袁辉辉的技术博客
安卓源码
int
main(
int
argc,
char
** argv) {
if
(!
strcmp
(basename(argv[0]),
"ueventd"
)) {
return
ueventd_main(argc, argv);
}
if
(!
strcmp
(basename(argv[0]),
"watchdogd"
)) {
return
watchdogd_main(argc, argv);
}
if
(REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
}
int
main(
int
argc,
char
** argv) {
if
(!
strcmp
(basename(argv[0]),
"ueventd"
)) {
return
ueventd_main(argc, argv);
}
if
(!
strcmp
(basename(argv[0]),
"watchdogd"
)) {
return
watchdogd_main(argc, argv);
}
if
(REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
}
add_environment(
"PATH"
, _PATH_DEFPATH);
bool
is_first_stage = (
getenv
(
"INIT_SECOND_STAGE"
) == nullptr);
if
(is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
umask(0);
mount(
"tmpfs"
,
"/dev"
,
"tmpfs"
, MS_NOSUID,
"mode=0755"
);
mkdir(
"/dev/pts"
, 0755);
mkdir(
"/dev/socket"
, 0755);
mount(
"devpts"
,
"/dev/pts"
,
"devpts"
, 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount(
"proc"
,
"/proc"
,
"proc"
, 0,
"hidepid=2,gid="
MAKE_STR(AID_READPROC));
chmod(
"/proc/cmdline"
, 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount(
"sysfs"
,
"/sys"
,
"sysfs"
, 0, NULL);
mount(
"selinuxfs"
,
"/sys/fs/selinux"
,
"selinuxfs"
, 0, NULL);
mknod(
"/dev/kmsg"
, S_IFCHR | 0600, makedev(1, 11));
mknod(
"/dev/random"
, S_IFCHR | 0666, makedev(1, 8));
mknod(
"/dev/urandom"
, S_IFCHR | 0666, makedev(1, 9));
InitKernelLogging(argv);
LOG(INFO) <<
"init first stage started!"
;
if
(!DoFirstStageMount()) {
LOG(ERROR) <<
"Failed to mount required partitions early ..."
;
panic();
}
SetInitAvbVersionInRecovery();
selinux_initialize(
true
);
if
(selinux_android_restorecon(
"/init"
, 0) == -1) {
PLOG(ERROR) <<
"restorecon failed"
;
security_failure();
}
setenv(
"INIT_SECOND_STAGE"
,
"true"
, 1);
static
constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv(
"INIT_STARTED_AT"
, std::to_string(start_ms).c_str(), 1);
char
* path = argv[0];
char
* args[] = { path, nullptr };
execv(path, args);
PLOG(ERROR) <<
"execv(\""
<< path <<
"\") failed"
;
security_failure();
}
add_environment(
"PATH"
, _PATH_DEFPATH);
bool
is_first_stage = (
getenv
(
"INIT_SECOND_STAGE"
) == nullptr);
if
(is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
umask(0);
mount(
"tmpfs"
,
"/dev"
,
"tmpfs"
, MS_NOSUID,
"mode=0755"
);
mkdir(
"/dev/pts"
, 0755);
mkdir(
"/dev/socket"
, 0755);
mount(
"devpts"
,
"/dev/pts"
,
"devpts"
, 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount(
"proc"
,
"/proc"
,
"proc"
, 0,
"hidepid=2,gid="
MAKE_STR(AID_READPROC));
chmod(
"/proc/cmdline"
, 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount(
"sysfs"
,
"/sys"
,
"sysfs"
, 0, NULL);
mount(
"selinuxfs"
,
"/sys/fs/selinux"
,
"selinuxfs"
, 0, NULL);
mknod(
"/dev/kmsg"
, S_IFCHR | 0600, makedev(1, 11));
mknod(
"/dev/random"
, S_IFCHR | 0666, makedev(1, 8));
mknod(
"/dev/urandom"
, S_IFCHR | 0666, makedev(1, 9));
InitKernelLogging(argv);
LOG(INFO) <<
"init first stage started!"
;
if
(!DoFirstStageMount()) {
LOG(ERROR) <<
"Failed to mount required partitions early ..."
;
panic();
}
SetInitAvbVersionInRecovery();
selinux_initialize(
true
);
if
(selinux_android_restorecon(
"/init"
, 0) == -1) {
PLOG(ERROR) <<
"restorecon failed"
;
security_failure();
}
setenv(
"INIT_SECOND_STAGE"
,
"true"
, 1);
static
constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv(
"INIT_STARTED_AT"
, std::to_string(start_ms).c_str(), 1);
char
* path = argv[0];
char
* args[] = { path, nullptr };
execv(path, args);
PLOG(ERROR) <<
"execv(\""
<< path <<
"\") failed"
;
security_failure();
}
InitKernelLogging(argv);
LOG(INFO) <<
"init second stage started!"
;
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
close(open(
"/dev/.booting"
, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
property_init();
process_kernel_dt();
process_kernel_cmdline();
export_kernel_boot_props();
property_set(
"ro.boottime.init"
,
getenv
(
"INIT_STARTED_AT"
));
property_set(
"ro.boottime.init.selinux"
,
getenv
(
"INIT_SELINUX_TOOK"
));
const
char
* avb_version =
getenv
(
"INIT_AVB_VERSION"
);
if
(avb_version) property_set(
"ro.boot.avb_version"
, avb_version);
unsetenv(
"INIT_SECOND_STAGE"
);
unsetenv(
"INIT_STARTED_AT"
);
unsetenv(
"INIT_SELINUX_TOOK"
);
unsetenv(
"INIT_AVB_VERSION"
);
selinux_initialize(
false
);
selinux_restore_context();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if
(epoll_fd == -1) {
PLOG(ERROR) <<
"epoll_create1 failed"
;
exit
(1);
}
signal_handler_init();
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
set_usb_controller();
const
BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();
parser.AddSectionParser(
"service"
, std::make_unique<ServiceParser>(&sm));
parser.AddSectionParser(
"on"
, std::make_unique<ActionParser>(&am));
parser.AddSectionParser(
"import"
, std::make_unique<ImportParser>(&parser));
std::string bootscript = GetProperty(
"ro.boot.init_rc"
,
""
);
if
(bootscript.empty()) {
parser.ParseConfig(
"/init.rc"
);
parser.set_is_system_etc_init_loaded(
parser.ParseConfig(
"/system/etc/init"
));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig(
"/vendor/etc/init"
));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig(
"/odm/etc/init"
));
}
else
{
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(
true
);
parser.set_is_vendor_etc_init_loaded(
true
);
parser.set_is_odm_etc_init_loaded(
true
);
}
if
(
false
) DumpState();
am.QueueEventTrigger(
"early-init"
);
am.QueueBuiltinAction(wait_for_coldboot_done_action,
"wait_for_coldboot_done"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
am.QueueBuiltinAction(set_mmap_rnd_bits_action,
"set_mmap_rnd_bits"
);
am.QueueBuiltinAction(set_kptr_restrict_action,
"set_kptr_restrict"
);
am.QueueBuiltinAction(keychord_init_action,
"keychord_init"
);
am.QueueBuiltinAction(console_init_action,
"console_init"
);
am.QueueEventTrigger(
"init"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
std::string bootmode = GetProperty(
"ro.bootmode"
,
""
);
if
(bootmode ==
"charger"
) {
am.QueueEventTrigger(
"charger"
);
}
else
{
am.QueueEventTrigger(
"late-init"
);
}
am.QueueBuiltinAction(queue_property_triggers_action,
"queue_property_triggers"
);
InitKernelLogging(argv);
LOG(INFO) <<
"init second stage started!"
;
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
close(open(
"/dev/.booting"
, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
property_init();
process_kernel_dt();
process_kernel_cmdline();
export_kernel_boot_props();
property_set(
"ro.boottime.init"
,
getenv
(
"INIT_STARTED_AT"
));
property_set(
"ro.boottime.init.selinux"
,
getenv
(
"INIT_SELINUX_TOOK"
));
const
char
* avb_version =
getenv
(
"INIT_AVB_VERSION"
);
if
(avb_version) property_set(
"ro.boot.avb_version"
, avb_version);
unsetenv(
"INIT_SECOND_STAGE"
);
unsetenv(
"INIT_STARTED_AT"
);
unsetenv(
"INIT_SELINUX_TOOK"
);
unsetenv(
"INIT_AVB_VERSION"
);
selinux_initialize(
false
);
selinux_restore_context();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if
(epoll_fd == -1) {
PLOG(ERROR) <<
"epoll_create1 failed"
;
exit
(1);
}
signal_handler_init();
property_load_boot_defaults();
export_oem_lock_status();
start_property_service();
set_usb_controller();
const
BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();
parser.AddSectionParser(
"service"
, std::make_unique<ServiceParser>(&sm));
parser.AddSectionParser(
"on"
, std::make_unique<ActionParser>(&am));
parser.AddSectionParser(
"import"
, std::make_unique<ImportParser>(&parser));
std::string bootscript = GetProperty(
"ro.boot.init_rc"
,
""
);
if
(bootscript.empty()) {
parser.ParseConfig(
"/init.rc"
);
parser.set_is_system_etc_init_loaded(
parser.ParseConfig(
"/system/etc/init"
));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig(
"/vendor/etc/init"
));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig(
"/odm/etc/init"
));
}
else
{
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(
true
);
parser.set_is_vendor_etc_init_loaded(
true
);
parser.set_is_odm_etc_init_loaded(
true
);
}
if
(
false
) DumpState();
am.QueueEventTrigger(
"early-init"
);
am.QueueBuiltinAction(wait_for_coldboot_done_action,
"wait_for_coldboot_done"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
am.QueueBuiltinAction(set_mmap_rnd_bits_action,
"set_mmap_rnd_bits"
);
am.QueueBuiltinAction(set_kptr_restrict_action,
"set_kptr_restrict"
);
am.QueueBuiltinAction(keychord_init_action,
"keychord_init"
);
am.QueueBuiltinAction(console_init_action,
"console_init"
);
am.QueueEventTrigger(
"init"
);
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action,
"mix_hwrng_into_linux_rng"
);
std::string bootmode = GetProperty(
"ro.bootmode"
,
""
);
if
(bootmode ==
"charger"
) {
am.QueueEventTrigger(
"charger"
);
}
else
{
am.QueueEventTrigger(
"late-init"
);
}
am.QueueBuiltinAction(queue_property_triggers_action,
"queue_property_triggers"
);
while
(
true
) {
int
epoll_timeout_ms = -1;
if
(do_shutdown && !shutting_down) {
do_shutdown =
false
;
if
(HandlePowerctlMessage(shutdown_command)) {
shutting_down =
true
;
}
}
if
(!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}
if
(!(waiting_for_prop || sm.IsWaitingForExec())) {
if
(!shutting_down) restart_processes();
if
(process_needs_restart_at != 0) {
epoll_timeout_ms = (process_needs_restart_at -
time
(nullptr)) * 1000;
if
(epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
if
(am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
int
nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if
(nr == -1) {
PLOG(ERROR) <<
"epoll_wait failed"
;
}
else
if
(nr == 1) {
((
void
(*)()) ev.data.ptr)();
}
}
return
0;
}
while
(
true
) {
int
epoll_timeout_ms = -1;
if
(do_shutdown && !shutting_down) {
do_shutdown =
false
;
if
(HandlePowerctlMessage(shutdown_command)) {
shutting_down =
true
;
}
}
if
(!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}
if
(!(waiting_for_prop || sm.IsWaitingForExec())) {
if
(!shutting_down) restart_processes();
if
(process_needs_restart_at != 0) {
epoll_timeout_ms = (process_needs_restart_at -
time
(nullptr)) * 1000;
if
(epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
if
(am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
int
nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if
(nr == -1) {
PLOG(ERROR) <<
"epoll_wait failed"
;
}
else
if
(nr == 1) {
((
void
(*)()) ev.data.ptr)();
}
}
return
0;
}
int
main(
int
argc,
char
*
const
argv[])
{
if
(!LOG_NDEBUG) {
String8 argv_String;
for
(
int
i = 0; i < argc; ++i) {
argv_String.append(
"\""
);
argv_String.append(argv[i]);
argv_String.append(
"\" "
);
}
ALOGV(
"app_process main with argv: %s"
, argv_String.c_str());
}
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
argc--;
argv++;
const
char
* spaced_commands[] = {
"-cp"
,
"-classpath"
};
bool
known_command =
false
;
int
i;
for
(i = 0; i < argc; i++) {
if
(known_command ==
true
) {
runtime.addOption(strdup(argv[i]));
ALOGV(
"app_process main add known option '%s'"
, argv[i]);
known_command =
false
;
continue
;
}
for
(
int
j = 0;
j <
static_cast
<
int
>(
sizeof
(spaced_commands) /
sizeof
(spaced_commands[0]));
++j) {
if
(
strcmp
(argv[i], spaced_commands[j]) == 0) {
known_command =
true
;
ALOGV(
"app_process main found known command '%s'"
, argv[i]);
}
}
if
(argv[i][0] !=
'-'
) {
break
;
}
if
(argv[i][1] ==
'-'
&& argv[i][2] == 0) {
++i;
break
;
}
runtime.addOption(strdup(argv[i]));
ALOGV(
"app_process main add option '%s'"
, argv[i]);
}
bool
zygote =
false
;
bool
startSystemServer =
false
;
bool
application =
false
;
String8 niceName;
String8 className;
++i;
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
;
}
}
Vector<String8> args;
if
(!className.empty()) {
args.add(application ? String8(
"application"
) : String8(
"tool"
));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
if
(!LOG_NDEBUG) {
String8 restOfArgs;
char
*
const
* argv_new = argv + i;
int
argc_new = argc - i;
for
(
int
k = 0; k < argc_new; ++k) {
restOfArgs.append(
"\""
);
restOfArgs.append(argv_new[k]);
restOfArgs.append(
"\" "
);
}
ALOGV(
"Class name = %s, args = %s"
, className.c_str(), restOfArgs.c_str());
}
}
else
{
maybeCreateDalvikCache();
if
(startSystemServer) {
args.add(String8(
"start-system-server"
));
}
char
prop[PROP_VALUE_MAX];
if
(property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
LOG_ALWAYS_FATAL(
"app_process: Unable to determine ABI list from property %s."
,
ABI_LIST_PROPERTY);
return
11;
}
String8 abiFlag(
"--abi-list="
);
abiFlag.append(prop);
args.add(abiFlag);
for
(; i < argc; ++i) {
args.add(String8(argv[i]));
}
}
if
(!niceName.empty()) {
runtime.setArgv0(niceName.c_str(),
true
);
}
if
(zygote) {
runtime.start(
"com.android.internal.os.ZygoteInit"
, args, zygote);
}
else
if
(!className.empty()) {
runtime.start(
"com.android.internal.os.RuntimeInit"
, args, zygote);
}
else
{
fprintf
(stderr,
"Error: no class name or --zygote supplied.\n"
);
app_usage();
LOG_ALWAYS_FATAL(
"app_process: no class name or --zygote supplied."
);
}
}
int
main(
int
argc,
char
*
const
argv[])
{
if
(!LOG_NDEBUG) {
String8 argv_String;
for
(
int
i = 0; i < argc; ++i) {
argv_String.append(
"\""
);
argv_String.append(argv[i]);
argv_String.append(
"\" "
);
}
ALOGV(
"app_process main with argv: %s"
, argv_String.c_str());
}
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
argc--;
argv++;
const
char
* spaced_commands[] = {
"-cp"
,
"-classpath"
};
bool
known_command =
false
;
int
i;
for
(i = 0; i < argc; i++) {
if
(known_command ==
true
) {
runtime.addOption(strdup(argv[i]));
ALOGV(
"app_process main add known option '%s'"
, argv[i]);
known_command =
false
;
continue
;
}
for
(
int
j = 0;
j <
static_cast
<
int
>(
sizeof
(spaced_commands) /
sizeof
(spaced_commands[0]));
++j) {
if
(
strcmp
(argv[i], spaced_commands[j]) == 0) {
known_command =
true
;
ALOGV(
"app_process main found known command '%s'"
, argv[i]);
}
}
if
(argv[i][0] !=
'-'
) {
break
;
}
if
(argv[i][1] ==
'-'
&& argv[i][2] == 0) {
++i;
break
;
}
runtime.addOption(strdup(argv[i]));
ALOGV(
"app_process main add option '%s'"
, argv[i]);
}
bool
zygote =
false
;
bool
startSystemServer =
false
;
bool
application =
false
;
String8 niceName;
String8 className;
++i;
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
;
}
}
Vector<String8> args;
if
(!className.empty()) {
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2025-3-13 22:10
被xianyuuuan编辑
,原因: