Android Zygote 源码剖析
1. 关于 Zygote
Zygote 是 Android 中的一个特殊进程,负责管理和高效地启动所有应用程序。它的名字来源于生物学中的“受精卵”,寓意为“所有 Android 应用进程的起源”。
1.1 Zygote 的核心特点
1.1.1 预加载
- Zygote 在启动时会预加载常用的类、资源和库(如 android.app.* 和 android.view.*),以便加速后续的应用启动。
- 通过这种方式,应用程序无需重新加载这些资源,从而减少内存占用和启动时间。
1.1.2 进程 fork
- Zygote 使用 Unix 的 fork() 系统调用来高效地创建新的子进程。子进程继承了 Zygote 的内存和虚拟机状态。这使得每个应用的启动更加快速和轻量。
1.1.3 内存共享
- 使用 fork() 时,Zygote 和子进程会共享一些只读的内存区域(如预加载的类和资源),这大幅降低了 Android 系统的内存使用。
1.2 Zygote 在 Android 系统中的作用
1.2.1 创建应用进程
- Zygote 是所有 Android 应用进程的父进程
- 当用户打开一个应用时,系统服务(如 ActivityManagerService)会向 Zygote 发出请求,Zygote 会通过 fork() 创建一个新进程
- 子进程的入口是 Java 层的 ActivityThread.main() 方法,负责初始化和启动应用
1.2.2 启动系统服务
- 在 Android 启动过程中,Zygote 会通过调用 SystemServer 启动关键的系统服务,例如:
- ActivityManagerService
- WindowManagerService
- PackageManagerService
- 这些服务是整个 Android 系统的核心
1.2.3 管理进程生命周期
- 通过与系统服务的配合,Zygote 实现了对 Android 应用进程的统一管理
- 当应用需要被杀死、重启时,Zygote 是应用生命周期管理的重要参与者
1.2.4 资源优化
- Zygote 的预加载和内存共享机制,显著优化了系统的资源利用率,确保多个应用能够高效运行
2. Zygote 启动过程及源码分析
2.1 Zygote 相关的 rc 启动脚本
- 以下是关于在 init.zygote64.rc 中描述的关于 zygote 服务的参数:会被导入进 init.rc 中,然后被 init 进程启动执行。
- system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
- 主要关注以下启动参数:
-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
- 解释如下:
-Xzygote
: 切换虚拟机到 Zygote 模式,为预加载类和资源、内存共享以及应用进程的快速启动做好准备。--start-system-server
:表示 zygote 会启动 SystemServer 进程,它是 Android 系统的核心服务进程。--socket-name
:系统可以通过 zygote 这个 socket 名字与 Zygote 进程进行通信。--zygote
: 一个标记,用于标识是 zygote 进程,然后会调用 ZygoteInit.main() 方法启动 Zygote 逻辑。
2.2 Zygote 整体启动框架
- frameworks/base/cmds/app_process/Android.bp
cc_binary {name: "app_process",srcs: ["app_main.cpp"],multilib: {lib32: {suffix: "32",},lib64: {suffix: "64",},},......
- frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
......runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
......
}
2.3 Zygote 启动流程
- 启动 Zygote 或其它普通非 framework 依赖的 java 程序的流程:
2.3.1 关于 JVM 初始化的一些细节
- libart.so 是 Android 系统中的一个核心动态链接库,负责实现 Android Runtime(ART)的主要功能。它是 Android 的虚拟机运行时库,用于运行 Java 应用程序的字节码,并为应用提供虚拟机环境。
- libart.so 是 Android 从 Dalvik 虚拟机(DVM)迁移到 ART 后的核心组件之一。
2.3.2 JNI 层相关知识点扫盲
2.3.2.1 JNIEnv 和 JavaVM 的基本概念
- 什么是 JNIEv* 对象
- 它是一个线程本地(thread-local)的接口指针,表示当前线程与 JVM 的交互上下文。
- 每个线程与 JVM 交互时都会有一个独立的 JNIEnv* 对象,用于执行 JNI 调用。
- JNIEv* 不是跨线程共享的。如果在多个线程中需要 JNI 操作,则每个线程需要获取自己的 JNIEv*。
- 什么是 JavaVM* 对象
- 它是 JVM 的全局变量,通常在进程中是单例的。
- 一个进程内所有线程共享同一个 JavaVM* 对象。
- JavaVM* 提供了创建线程、附加现有线程到 JVM 的方法等全局操作。
- 在 APK 中的情况是怎样的:
- JNIEv* 对象
- 每个线程在第一次进入 JNI 时,JVM 会为该线程分配一个 JNIEnv*。
- 因此,一个 APK(进程)中可能有多个 JNIEv* 对象,每个线程一个。
- 如果某个线程不再需要与 JVM 交互,则它的 JNIEv* 会被回收。
- JavaVM* 对象
- 一个 APK(进程)只有一个 JavaVM* 对象,代表该进程的 JVM 实例。
- 无论有多少个线程或 JNIEv* 对象,它们都共享这个唯一的 JavaVM*。
- JNIEv* 对象
- 如何从 JavaVM* 获取 JNIEv*:
JNIEnv* env;
javaVm->AttachCurrentThread(&env, nullptr); // 获取当前线程的 JNIEnv*
2.3.2.2 关于 JNI 注册机制
- 关于 JNI 的注册机制,是将本地(Native)C++方法绑定到特定的 Java 类上,以便 Java 层能够调用对应的本地实现。
- 解释接口:jniRegisterNativeMethods
/*
** env:JNIEv*
** className:要注册方法的Java类的全限定名(com/android/internal/os/RuntimeInit)
** methods:包含方法定义的数组,属于JNINativeMethod类型的数组
** NELEM(methods):数组长度
*/
int jniRegisterNativeMethods(JNIEnv* env, const char* className,const JNINativeMethod* methods, int numMethods);jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", methods, NELEM(methods));
- 对于 JNINativeMethod:
typedef struct {const char* name;const char* signature;void* fnPtr;
} JNINativeMethod;
- name:本地方法的名字,必须与 Java 层声明的本地方法名字一致。
- signature:方法的签名,描述参数类型和返回值类型。
- fnptr:本地方法的指针,指向 C/C++ 层实现的方法。
2.3.2.3 实战中的注册实现过程
以此为例进行说明:源码文件位置在 frameworks/base/core/jni/AndroidRuntime.cpp
。
// frameworks/base/core/jni/AndroidRuntime.cpp
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{const JNINativeMethod methods[] = {{"nativeFinishInit", "()V",(void*)com_android_internal_os_RuntimeInit_nativeFinishInit},{"nativeSetExitWithoutCleanup", "(Z)V",(void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},};return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",methods, NELEM(methods));
}
- 在 Java 层声明本地方法:在 Java 类 com.android.internal.os.RuntimeInit 中声明如下方法:
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
private static final native void nativeFinishInit();private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
- C/C++ 层(JNI层)实现方法:提供与 Java 方法对应的本地实现:
// frameworks/base/core/jni/AndroidRuntime.cpp
static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
{// 设置初始化的本地逻辑
}static void com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup(JNIEnv* env,jobject clazz, jboolean exitWithoutCleanup)
{// 设置是否跳过清理的逻辑
}
- 通过 register_com_android_internal_os_RuntimeInit 注册:在初始化阶段,调用该函数将本地方法绑定到 RuntimeInit 类。
// frameworks/base/core/jni/AndroidRuntime.cpp
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{const JNINativeMethod methods[] = {{"nativeFinishInit", "()V",(void*)com_android_internal_os_RuntimeInit_nativeFinishInit},{"nativeSetExitWithoutCleanup", "(Z)V",(void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},};return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",methods, NELEM(methods));
}
static const RegJNIRec gRegJNI[] = {......REG_JNI(register_com_android_internal_os_RuntimeInit),......
};/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{......if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {env->PopLocalFrame(NULL);return -1;}......
}
AndroidRuntime::startReg
⇒ register_jni_procs(gRegJNI, NELEM(gRegJNI), env)
⇒ register_com_android_internal_os_RuntimeInit
⇒ jniRegisterNativeMethods(env, “com/android/internal/os/RuntimeInit”, methods, NELEM(methods));
- Java 层调用 JNI 过程:Java 层通过调用 nativeFinishinit() 和 nativeSetExitWithoutCleanup(boolean),触发对应的本地方法。
2.4 总结 Zygote 启动流程
- Zygote 进程是由 init 启动的,Zygote 的启动是通过 init 进程完成的,init.rc 文件中定义了启动命令,启动的程序是 app_process64,它是一个 C++ 程序,是 Zygote 的 C++ 部分。app_process64 的主要任务是:
- 初始化个启动 JVM(Java 虚拟机)
- 注册 JNI 接口,用于桥接 Native C++ 层和 Framework Java 层
- 调用 ZygoteInit.main() 进入 Java 层
- 一旦完成了 C++ 层的初始化,app_propcess64 会将控制权交给 Java 层的 ZygoteInit.main(),从此进入 Java 代码的执行。
- Zygote 的主流程从 C++ 层最终到 Java 层,并且后面的主要逻辑(如 fork 子进程、启动 system_server 等)都发生在 Java 层,但是 C++ 层仍然存在,且保持活跃:C++ 层的 ART 虚拟机(libart.so)负责解释执行 Java 字节码,并为 JNI 调用提供支持。所以理解为:Zygote 进程的逻辑主要在 Java 层执行,但 C++ 层的 ART 是它运行的基础。
3. ZygoteInit Java 层主流程分析
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
3.1.1 初始化阶段
3.1.1.1 先进行 RuntimeTimeInit.preForkInit()
- RuntimeTimeInit.preForkInit() 专门为 fork 子进程(即应用进程)之前准备环境。
- 主要是 DDMS 的相关 handler 消息注册:
// frameworks/base/core/java/android/ddm/DdmRegister.javapublic static void registerHandlers() {if (false)Log.v("ddm", "Registering DDM message handlers");DdmHandleHello.register(); // 处理hello消息,主要是初始握手和功能发现DdmHandleHeap.register(); // 处理内存堆的调试消息DdmHandleNativeHeap.register(); // 处理Native堆的调试消息DdmHandleProfiling.register(); // 处理性能分析的消息DdmHandleExit.register(); // 处理进程退出的消息DdmHandleViewDebug.register(); // 处理view调试相关的消息DdmServer.registrationComplete(); // 通知DdmServer所有消息处理器已注册完成}
- 扩展:关于 DDMS、DdmServer、ADB、ADBD 和应用 APP 之间关系架构如下
3.1.1.2 再进行预加载资源 preload()
preload() 这个函数在 Zygote 启动时加载系统框架所需的资源和类。
static void preload(TimingsTraceLog bootTimingsTraceLog) {Log.d(TAG, "begin preload");bootTimingsTraceLog.traceBegin("BeginPreload");beginPreload();bootTimingsTraceLog.traceEnd(); // BeginPreloadbootTimingsTraceLog.traceBegin("PreloadClasses");preloadClasses();bootTimingsTraceLog.traceEnd(); // PreloadClassesbootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");cacheNonBootClasspathClassLoaders();bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoadersbootTimingsTraceLog.traceBegin("PreloadResources");preloadResources();bootTimingsTraceLog.traceEnd(); // PreloadResourcesTrace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");nativePreloadAppProcessHALs();Trace.traceEnd(Trace.TRACE_TAG_DALVIK);Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadGraphicsDriver");maybePreloadGraphicsDriver();Trace.traceEnd(Trace.TRACE_TAG_DALVIK);preloadSharedLibraries();preloadTextResources();// Ask the WebViewFactory to do any initialization that must run in the zygote process,// for memory sharing purposes.WebViewFactory.prepareWebViewInZygote();endPreload();warmUpJcaProviders();Log.d(TAG, "end preload");sPreloadComplete = true;}
3.1.1.2.1 为什么 Zygote 需要预加载类和资源?
- 性能优化:通过提前预加载类、资源和共享库,减少子进程启动时的加载开销
- 内存共享:利用 Zygote 的 Copy-On-Write(COW)技术,所有预加载的资源都可以在子进程共享
3.1.1.2.2 什么是 Copy-On-Write 机制?原理是啥?
- 在 Android 中,COW 机制主要用于 Zygote 进程及其 fork 出的子进程(应用进程)的内存管理,是优化系统性能和内存使用的重要机制。
- 共享数据:如果多个进程需要使用相同的数据(如类、资源、共享库),这些数据会被映射到相同的内存区域。只要这些数据没有被修改,不需要为每个进程赋值一份,所有进程共享一片物理内存。
- 写时复制:当某个进程试图修改共享的数据时,会触发一次内存页的复制操作。修改的数据会复制到该进程的独立内存中,从而避免影响其他共享该数据的进程。
- 触发条件:数据从只读变为可写时触发(例如,写操作),内存页标记为“写时复制页”,当检测到写操作时,内核会执行复制。
3.1.1.2.3 预加载的主要是哪些内容
- 类加载(
preloadClasses()
):优化 Java 层框架类的加载。- 主要加载哪些类:常用的 JAVA 核心类,Android 框架类,系统服务类(android.os.*)等。
- 缓存非引导类工具 Jar 库(
cacheNonBootClasspathClassLoaders()
) - 资源加载(
preloadResources()
):减少资源解析时间。- 加载图片资源,颜色状态列表,以及多窗口主题相关的一些图片资源。
- Hal 加载(
nativePreloadAppProcessHALs()
):图层分配器的 hal 加载- 调用了这个接口:nativePreloadAppProcessHALs(); 从名字看出是一个 native 方法,当前文件位于
com/android/internal/os/ZygoteInit.java
。 - 对应的 JNI 层文件名为
android_internal_os_ZygoteInit_nativePreloadAppProcessHALs()
。
- 调用了这个接口:nativePreloadAppProcessHALs(); 从名字看出是一个 native 方法,当前文件位于
- 图形渲染驱动加载(
maybePreloadGraphicsDriver()
):- 调用
nativePreloadGraphicsDriver()
。 - JNI 层调用
android_internal_os_ZygoteInit_nativePreloadGraphicsDriver()
。 - 接着调用
zygote_preload_graphics()
。 - 目的是,在系统初始化时加载必要的图形驱动(如 OpenGL 或 Vulkan),为后续的渲染任务(如 UI 绘制)做好准备,从而提升系统和应用的启动性能。
- 调用
- 共享库加载(
preloadSharedLibraries()
)System.loadLibrary("android");
,加载 libandroid.so,这是 Android 系统的核心库之一,提供许多底层功能。System.loadLibrary("jnigraphics");
,加载 libjnigraphics.so,用于图形相关的 JNI 接口。
- 关于预热 JCA provider(
warmUpJcaProviders()
)- JCA:JAVA 加解密框架
- Security(管理者)
- Provider 1:SunJCE(实现 AES、RSA 等)
- Provider 2:AndroidKeyStoreProvider(实现 KeyStore 加密)
- Provider 3:BC(实现更多算法)
- …
- Cipher,signature,KeyStore,MessageDigest 等(统一的加密 API)
- Security(管理者)
- 这里涉及到加解密功能,JCA 就像一个工具箱,对应用来说,它可以提供加密、解密等功能,应用只需要知道工具的功能,不需要知道工具怎么来的。
- JCA:JAVA 加解密框架
3.1.1.3 最后执行垃圾回收 GC
- 在 fork 子进程之前,调用 GC 清理不必要的对象,释放内存。
- 减少 Zygote 进程中无用对象的数量,避免这些对象被 fork 到子进程。(内存优化)
- 保证 Native 和 Java 资源都已妥善释放,避免资源泄露。(资源释放)
- 为子进程提供一个轻量的启动环境,减少不必要的内存负担。(性能更好)
3.1.2 Zygote 进程间通信的相关 socket
主要依靠以下调用来完成:
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
Zygote.initNativeState(isPrimaryZygote);
// frameworks/base/core/java/com/android/internal/os/Zygote.java
static void initNativeState(boolean isPrimary) {nativeInitNativeState(isPrimary);
}
// frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
static const JNINativeMethod gMethods[] = {......(void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},{"nativeInitNativeState", "(Z)V", (void*)com_android_internal_os_Zygote_nativeInitNativeState},......
}static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jclass,jboolean is_primary) {gZygoteSocketFD =android_get_control_socket(is_primary ? "zygote" : "zygote_secondary");......gUsapPoolSocketFD =android_get_control_socket(is_primary ? "usap_pool_primary" : "usap_pool_secondary");......initUnsolSocketToSystemServer();......
}static void initUnsolSocketToSystemServer() {gSystemServerSocketFd = socket(AF_LOCAL, SOCK_DGRAM | SOCK_NONBLOCK, 0);......
}
- 备注:USAP 的作用
- Android 10 开始,引入了 USAP Pool (Unspecialized App Process Pool):
- Zygote 会预先 fork 出一些「半成品」的 App 进程(usap)。
- 它们还没绑定具体应用,只是空壳,里面只包含 Zygote 的初始化内容。
- 当要启动新 App 时,直接把这些 usap “特化”(specialize),完成剩下的初始化并运行目标应用。
- 这样就减少了每次 fork 的开销,提高 冷启动速度
3.1.3 Zygote 如何 fork 出 system-server 进程
3.1.3.1 关于 ProcessState 对象
pid = Zygote.forkSystemServer()
zygoteServer.closeServerSocket();
handleSystemServerProcess(parsedArgs)
⇒ ZygoteInit.zygoteInit()⇒ ZygoteInit.nativeZygoteInit();⇒ com_android_internal_os_ZygoteInit_nativeZygoteInit()⇒ gCurRuntime->onZygoteInit();⇒ sp<ProcessState> proc = ProcessState::self();⇒ proc->startThreadPool();
- ProcessState 是 Android Binder 机制的核心组件之一,主要负责为进程与 Binder 驱动通信的能力。在Android 系统中,App 进程依赖 ProcessState 实现进程间通信(IPC),每个 App 进程都会通过 ProcessState 初始化与 Binder 驱动的连接,通过 Binder,App 可以与其他进程(如系统进程、服务进程等)进行通信。
- 所以每个 APP 进程,无论是系统的还是普通应用 APP 进程,其实内部在 C++ 层都持有一个 ProcessState 对象,它代表着一个进程对象与其它进程进行 Binder 通信的一个实例。
- 以下是其初始化调用链:在初始化过程中,主要在构造函数中做了三件事:
- 打开驱动
- 设置线程池大小
- 内存映射,大小为 1M
ProcessState::self()
⇒ init(kDefaultDriver, false /*requireDefault*/);⇒ gProcess = sp<ProcessState>::make(driver); // 实例化对象⇒ ProcessState::ProcessState(driver); //调用构造函数⇒ open_driver(driver); // 打开binder驱动⇒ status_t result = ioctl(fd, BINDER_VERSION, &vers); // 指定binder版本⇒ result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); // 设置binder线程池大小为15个⇒ mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, opened.value(), 0); // BINDER_VM_SIZE = 1M size
3.1.3.2 关于 Binder 线程池的创建过程
在前面的 ProcessState 源码分析过程中,主要是下面流程:
sp<ProcessState> proc = ProcessState::self();
proc->startThreadPool(); //这里开始分析这个接口
以下是具体的分析调用过程:
proc->startThreadPool();
⇒ ProcessState::startThreadPool()⇒ spawnPooledThread(true);⇒ String8 name = makeBinderThreadName(); // 获得一个线程的名字⇒ sp<Thread> t = sp<PoolThread>::make(isMain); // 实例化一个PoolThread线程⇒ t->run(name.string()); // 运行这个线程
我们再进入 PoolThread 类的代码进一步阅读:
class PoolThread : public Thread
{
public:explicit PoolThread(bool isMain): mIsMain(isMain){}protected:virtual bool threadLoop(){IPCThreadState::self()->joinThreadPool(mIsMain);return false;}const bool mIsMain;
};
这个 PoolThread 是继承于 Thread 父类,所以它是一个线程对象,当调用父类 run 方法时,threadLoop() 将会被调用,因此我们继续跟进 IPCThreadState::self()->joinThreadPool(mIsMain):
IPCThreadState::self()->joinThreadPool(mIsMain);
⇒ result = getAndExecuteCommand();
IPCThreadState 这个类对象表示进程中的一个线程对象,这边很清楚的展示了整个读取线程启动的过程,其实就是不断的从内核中读取发来的 binder 数据信息,并执行方法。
3.1.3.3 怎么理解 binder 线程池
前面通过源码分析,我们知道在实例化 ProcessState 时,当它调用 startThreadPool 方法时,会创建一个线程,这个是 binder 主线程,但是它只有一个线程,又如何理解这个线程池呢?多线程体现在哪里?我们可以深入分析 getAndExecuteCommand 这个接口:
result = getAndExecuteCommand();
⇒ result = talkWithDriver();⇒ ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) // 从内核的binder驱动中读数据
result = executeCommand(cmd); // 执行binder命令
⇒ case BR_SPAWN_LOOPER:mProcess->spawnPooledThread(false); // 这种情况binder驱动要求创建多线程// 通过线程中的进程对象ProcessState调用spawnPooledThread创建线程池
进一步思考,BR_SPAWN_LOOPER 这个情况是如何产生的呢?既然前面是 talkWithDriver(),说明这个数据来自binder驱动,所以需要跟进binder驱动代码,也就是内核binder驱动中会根据一些条件来触发这则消息来创建新的线程。
3.1.3.4 关于类反射机制调用类中的 main 方法
Java 的反射机制允许程序在运行时检查或操作类、接口、字段和方法,而无需在编译时确定。这在动态加载类、框架开发、工具实现等场景非常有用。
在前面启动 system server 案例中,主要有以下几个关键步骤:
Class<?> cl = Class.forName(className, true, classLoader); //Step 1
Method m;
m = cl.getMethod("main", new Class[] { String[].class }); //Step 2
int modifiers = m.getModifiers(); //Step 3
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { throw new RuntimeException("")
}
m.invoke(null, new Object[] { mArgs }); //Step 4
Step 1
方法原型:
Class<?> forName(String className, boolean initialize, ClassLoader loader)
className: String类型,完整限定类名(包括包名)
initialize:布尔型,是否在加载类的同时对类进行初始化,true:立即执行类的静态初始化块和静态变量初始化,反之填false
loader:用于加载类的类加载器如果为 null,默认使用系统类加载器(ClassLoader.getSystemClassLoader())可以使用自定义的 ClassLoader 来加载特定类
对于 system server 进程为说,这个 className 应该是:“com.android.server.SystemServer”,并且会执行此类的静态初始化块和静态变量的初始化。通过这个接口,我们得到了 SystemServer 的 Class 对象。
Step 2
Class 类的 getMethod 方法原型:
public Method getMethod(String name, Class<?>... parameterTypes)
参数1: name,方法的名字(这里为"main")
参数2:Class<?>... parameterTypes:该方法的参数类型数组对于 main 方法,其参数是一个 String[] 数组(public static void main(String[] args)),所以需要将参数类型显式传递为 String[].class。
为什么需要加 new Class[] {}
, getMethod 要求参数 2 是一个 Class<?> 类型的数组,用来描述方法的参数类型
new Class[] {}
:表示创建一个 Class 类型的数组{ String[].class }
:表示该数组里包含一个元素,类型是 String[]
总的来说,就是创建一个 Class 数组,数组中包含一个元素 String[].class
注意:String[].class:表示 String 类型的数组对应的 Class 对象,在反射中,需要用 Class 类型来描述方法的参数类型,而不是直接使用数据类型。- 如果方法接受 int 参数,需要传入 int.class。
- 如果方法接受 String 参数,需要传入 String.class。
- 如果方法接受 String[] 参数,需要传入 String[].class。
Step 3
检查方法修饰信息。
int modifiers = m.getModifiers();
表示获取方法 m 的修饰符信息,方法 getModifiers() 返回的是一个 int 类型,表示方法的修饰符位掩码
在 Java 中,修饰符用一组位标志表示,例如:
Modifier.PUBLIC:方法是 public。
Modifier.STATIC:方法是 static。
Modifier.FINAL:方法是 final。
通过这些标志,可以使用工具类 Modifier 提供的静态方法检查具体修饰符,就是上面代码中的:
Modifier.isStatic(modifiers)及Modifier.isPublic(modifiers)等
Step 4
使用 invoke 方法调用,执行 app 的 main() 方法。
示例
- Demo.java
package com.example;public class Demo {public static void main(String[] args) {System.out.println("Demo.main() 被执行!");if (args != null && args.length > 0) {System.out.println("传入的参数:");for (int i = 0; i < args.length; i++) {System.out.println(" args[" + i + "] = " + args[i]);}} else {System.out.println("未传入参数。");}}
}
- ReflectInvokeMain.java
package com.example;import java.lang.reflect.Method;public class ReflectInvokeMain {public static void main(String[] args) {try {// 1. 加载目标类(这里是 com.example.Demo)Class<?> targetClass = Class.forName("com.example.Demo");// 2. 获取 main 方法// main 方法的标准定义是:public static void main(String[] args)// 所以参数类型是 String[].classMethod mainMethod = targetClass.getMethod("main", String[].class);// 3. 组装参数// 注意:调用 mainMethod.invoke 时,如果直接传 mainArgs,会被拆开,导致参数不匹配。// 必须写成 (Object) mainArgs,强制作为一个整体数组传入String[] mainArgs = {"hello", "world", "from", "reflection"};// 4. 调用 main 方法System.out.println("通过反射调用 Demo.main() ...");mainMethod.invoke(null, (Object) mainArgs);} catch (Exception e) {e.printStackTrace();}}
}
- 运行效果
假设你在命令行编译并运行:
javac com/example/Demo.java com/example/ReflectInvokeMain.java
java com.example.ReflectInvokeMain
输出结果大致如下:
通过反射调用 Demo.main() ...
Demo.main() 被执行!
传入的参数:args[0] = helloargs[1] = worldargs[2] = fromargs[3] = reflection
3.1.4 Zygote 如何 fork 出普通 app 进程
Zygote 进程 fork 出普通 app 的程序核心逻辑位到 ZygoteServer.java中的runSelectLoop()方法。
3.1.4.1 USAP 工作流程逻辑梳理
在进入核心流程之前先来了解一下 usap 的工作逻辑:
- 进程池初始化(Status 1)
- 动作:
Zygote 预先通过fork()
创建n
个通用USAP子进程(数量由系统配置决定)。 - 状态:
所有子进程阻塞在Socket.accept()
,等待任务分配(无CPU消耗)。 - 设计目的:
避免每次启动应用时重复fork()
+ 类加载的开销,提升应用启动速度 30%+。
- 任务分配(Status 2)
- 触发条件:
AMS(Activity Manager Service)需要启动新应用时。 - 关键流程:
- AMS 通过 Unix Domain Socket 向 USAP 进程池发送启动参数(包名、UID等)。
- 内核调度唤醒一个阻塞在
accept()
的 USAP 子进程(如USAP子进程1
)。
- 进程专用化(Status 3)
- 核心操作:
- 子进程解析 AMS 传递的启动参数。
- 执行
specialize()
操作:- 绑定应用包名/UID
- 加载目标 APK 的 Dex/资源
- 初始化 App 专属的 ART 运行时
- 状态转换:
USAP 进程脱离进程池 → 成为独立应用进程(如com.tencent.mm
)。
- 进程回收与补充(Status 4)
- 回收机制:
- 应用进程退出时通过 Pipe管道 通知 Zygote。
- Zygote 立即
fork()
一个新的 USAP 子进程加入池中。
- 动态平衡:
始终保持进程池中有n
个就绪USAP(自适应内存水位)。
3.1.4.2 zygote 进程 runSelectLoop 运行逻辑:
程序的核心逻辑如下(包含 usap 池子及事件的管理):