当前位置: 首页 > news >正文

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*。
  • 如何从 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));
}
  1. 在 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);
  1. 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)
{// 设置是否跳过清理的逻辑
}
  1. 通过 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));

  1. 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()
  • 图形渲染驱动加载(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)
    • 这里涉及到加解密功能,JCA 就像一个工具箱,对应用来说,它可以提供加密、解密等功能,应用只需要知道工具的功能,不需要知道工具怎么来的。
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")
参数2Class<?>... 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:方法是 publicModifier.STATIC:方法是 staticModifier.FINAL:方法是 final。
通过这些标志,可以使用工具类 Modifier 提供的静态方法检查具体修饰符,就是上面代码中的:
Modifier.isStatic(modifiers)Modifier.isPublic(modifiers)
Step 4

使用 invoke 方法调用,执行 app 的 main() 方法。

示例
  1. 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("未传入参数。");}}
}
  1. 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();}}
}
  1. 运行效果
    假设你在命令行编译并运行:
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 的工作逻辑:
在这里插入图片描述

  1. 进程池初始化(Status 1)
  • 动作
    Zygote 预先通过 fork() 创建 n 个通用USAP子进程(数量由系统配置决定)。
  • 状态
    所有子进程阻塞在 Socket.accept(),等待任务分配(无CPU消耗)。
  • 设计目的
    避免每次启动应用时重复 fork() + 类加载的开销,提升应用启动速度 30%+
  1. 任务分配(Status 2)
  • 触发条件
    AMS(Activity Manager Service)需要启动新应用时。
  • 关键流程
    • AMS 通过 Unix Domain Socket 向 USAP 进程池发送启动参数(包名、UID等)。
    • 内核调度唤醒一个阻塞在 accept() 的 USAP 子进程(如 USAP子进程1)。
  1. 进程专用化(Status 3)
  • 核心操作
    • 子进程解析 AMS 传递的启动参数。
    • 执行 specialize() 操作:
      • 绑定应用包名/UID
      • 加载目标 APK 的 Dex/资源
      • 初始化 App 专属的 ART 运行时
  • 状态转换
    USAP 进程脱离进程池 → 成为独立应用进程(如 com.tencent.mm)。
  1. 进程回收与补充(Status 4)
  • 回收机制
    • 应用进程退出时通过 Pipe管道 通知 Zygote。
    • Zygote 立即 fork() 一个新的 USAP 子进程加入池中。
  • 动态平衡
    始终保持进程池中有 n 个就绪USAP(自适应内存水位)。
3.1.4.2 zygote 进程 runSelectLoop 运行逻辑:

程序的核心逻辑如下(包含 usap 池子及事件的管理):
在这里插入图片描述

3.1.4.3 主 socket 与 pipe 文件描述符部分的说明

在这里插入图片描述

http://www.xdnf.cn/news/1453879.html

相关文章:

  • webpack scope hositing 和tree shaking
  • 谷歌修复安卓120个漏洞,含两个正遭利用的零日漏洞
  • 一文吃透 C#中异步编程Task
  • 鸿蒙权限崩溃:网络文件访问全攻略
  • CentOS系统如何查看当前内存容量
  • android View详解—View的刷新流程源码解析
  • 惊!printf 不往屏幕输?都是 fd 在搞鬼!爆肝拆解 Linux 文件描述符 + 重定向底层,学会直接在终端横着走
  • STM32-UART-中断
  • Qt QJsonObject
  • 数据库集成:使用 SQLite 与 Electron
  • uni 拍照上传拍视频上传以及相册
  • jenkins调用ansible部署lnmp平台-Discuz论坛
  • Java 流程控制:从入门到面试的全方位指南
  • C语言(长期更新)第14讲:指针详解(四)
  • 【图像处理基石】如何在频域对图像进行处理和增强?
  • VSCode中的扩展Extension说明
  • 《计算机网络安全》实验报告一 现代网络安全挑战 拒绝服务与分布式拒绝服务攻击的演变与防御策略(2)
  • 深度学习:ResNet 残差神经网络详解
  • C++ 面试高频考点 力扣 153. 寻找旋转排序数组中的最小值 二分查找 题解 每日一题
  • 2025年GEO优化供应商盘点:五大实力派助您抢占AI搜索先机
  • 大数据框架对比与选择指南
  • Vulkan计算着色器中Dispatch、workGroups、invocation之间的关系
  • Docker(③MobaXterm连接WSL Ubuntu)
  • Flowable——流程定义与部署(RepositoryService)
  • Gamma AI:AI演示文稿制作工具,高效解决PPT框架搭建难与排版耗时问题
  • C# HTTP请求最佳实践
  • 关于亚马逊账号关联的新思考——账号注册注意事项
  • 基于单片机矿井安全检测/煤矿环境安全监测设计
  • Vue 3 学习路线指南
  • NVIDIA Jetson 开发板使用