Android12源码—— Zygote 启动SystemServer

Zygote 介绍

在 Android 系统中,DVM(Dalvik 虚拟机)/ART,系统服务进程 system_server 以及应用程序进程都是由 Zygote 进程来创建的(而 Native 程序,也就是 C/C++ 开发的程序则是由 init 进程创建启动的)。Zygote 进程也称孵化器,通过 fork(复制进程) 的形式来创建应用程序进程和 system_server 进程,由于 Zygote 进程在启动时会创建 DVM 或者 ART ,因此通过 fork 而创建的应用程序进程和 system_server 进程可以在内部获取一个 DVM 或者 ART 的实例副本。

Zygote 是一个 C/S模型。Zygote 进程作为服务端,主要负责创建 Java 虚拟机,加载系统资源,启动 system_server 进程以及后续运行过程中启动普通的应用程序进程。其他进程作为客户端向它发送 fork 请求,Zygote 进程接收到这个请求后会 fork 出一个新的进程。

Zygote 通过 Socket 的方式和其他进程进行通信,这里的"其他进程"主要指的是系统服务进程 system_server。

在 Linux 系统中,调用 fork 函数创建子进程的时候,不会复制父进程的内存,而是父子进程共享一个内存空间,只有当子进程或者父进程对内存数据进行修改时才会进行内存复制,从而,父进程和子进程才有各自的内存空间。在此之前,只会以只读的方式共享。

写时拷贝(copy-on-write):等到修改数据的时候才真正的分配内存空间,是一种可以推迟甚至避免拷贝数据的技术。这是对程序性能的优化,这样做的目的是为了避免不必要的拷贝。

Zygote 作为孵化器,可以提前加载一些资源,这样 fork 出的子进程就可以直接使用这些资源,而不用重新加载。比如,system_server 进程就可以直接使用 Zygote 进程中的 JNI 函数、共享库、常用类以及主题资源。

Zygote 启动过程

Zygote 启动文件回顾

在了解Zygote启动过程之前需要先了解系统启动时init过程,这里不做介绍,可以参考:XXX, 如果是64位机器,在系统启动时会加载system/core/rootdir/init.zygote64_32.rc 文件

perl 复制代码
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver //audioserver进程终止了就重启进程
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    task_profiles ProcessCapacityHigh MaxPerformance
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote_secondary stream 660 root system
    socket usap_pool_secondary stream 660 root system
    onrestart restart zygote
    task_profiles ProcessCapacityHigh MaxPerformance

Zygote 进程是在 init 进程启动时创建的,起初,Zygote 进程的名称并不是 Zygote,而是 app_process。Zygote 进程启动后,Linux 系统下的 pctrl 系统会调用 app_process,将其名称换成了 Zygote。

上面脚本说明: 1、zygote 要开启的进程名 2、app_process 要执行程序名,位置在/system/bin/路径下 3、main classname 4、如果audioserver、cameraserver、media等进程终止了,就需要进行restart(重启) 5、参数-Xzygote /system/bin --zygote --start-system-server

zygote启动入口

那么zygote启动的入口在哪里?Zygote启动的代码在Framework目录下frameworks/base/cmds/app_process/app_main.cpp, 下面看一下app_main.cppmain方法:

c 复制代码
int main(int argc, char* const argv[])
{
    ....// 省略部分代码
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ....// 省略部分代码

    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    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.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    ....// 省略了 args 参数生成过程

    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        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.");
    }
}

结合上面init.zygote64_32.rc文件和上面代码发现,在启动zygote的时候,代码中的 zygotestartSystemServer变量都会被设置为true, 导致在后面执行runtime.start("com.android.internal.os.ZygoteInit", args, zygote),那么runtime是什么呢?

AppRuntime

上面发现Zygote是在AppRuntime对象中被启动的,这里的AppRuntime继承AndroidRuntime并在frameworks/base/cmds/app_process/app_main.cpp中实现,但是没有重新实现start函数,那么就需要到AndroidRuntime中看一下。 AndroidRuntimeframeworks/base/core/jni/AndroidRuntime.cpp

scss 复制代码
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{

    static const String8 startSystemServer("start-system-server");
    bool primary_zygote = false;

    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
            primary_zygote = true;
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
        }
    }
    ....// 省略部分代码

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    // 启动虚拟机
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions. 为虚拟机注册 JNI 方法
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    // 从 app_main 的 main 函数得知 className 为 com.android.internel.os.ZygoteInit
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    //将 className 的 "." 替换为 "/"
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    // 找到 zygoteInit
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
    	// 找到 ZygoteInit 的 main 函数
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
        // 通过 JNI 调用 ZygoteInit 的 main 方法
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        ....// 省略部分代码
        }
    }
     ....// 省略部分代码
}

上面代码主要作用是通过虚拟机jni方式调用zygoteInitmain方法启动Zygote

ZygoteInit

ZygoteInit是个java类,位置在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java,下面看一下main函数中的关键实现:

ini 复制代码
    public static void main(String[] argv) {
        ZygoteServer zygoteServer = null;

        ....// 省略部分代码
        Runnable caller;
        try {
            ....// 省略部分代码

            boolean startSystemServer = false;
            String zygoteSocketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            // 第一步
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    enableLazyPreload = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }

             ....// 省略部分代码
            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }

            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                        SystemClock.uptimeMillis());
                //第二步
                preload(bootTimingsTraceLog); // 预加载类和资源
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                        SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            }
             ....// 省略部分代码
             // 第三步
            zygoteServer = new ZygoteServer(isPrimaryZygote); // 创建一个 Server 端的 Socket
            if (startSystemServer) {
                // 第四步
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);

                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                // child (system_server) process.
                if (r != null) {
                    r.run();
                    return;
                }
            }

             ....// 省略部分代码
             // 第五步
            caller = zygoteServer.runSelectLoop(abiList); // 等待 AMS/ATMS 请求
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with fatal exception", ex);
            throw ex;
        } finally {
            if (zygoteServer != null) {
                zygoteServer.closeServerSocket();
            }
        }

        // We're in the child process and have exited the select loop. Proceed to execute the
        // command.
        if (caller != null) {
            caller.run();
        }
    }

上面代码说明: 第一步:解析启动参数, 这里面的参数是init.zygote64_32.rc中, 会发现startSystemServer被设置为true, enableLazyPreload在的Zygote32启动时设置为了true 第二步:预加载类和资源, 通过init.zygote64_32.rc文件知道在Zygote64启动时enableLazyPreloadfalse , 在Zygote32启动时设置为了true,所以只有在Zygote64中会加载类和资源 第三步:创建一个Server 端的 Socket 第四步:启动System_servier 进程 第五步: 等待 AMS/ATMS 请求

ZygoteServer

下面看一下第三步的ZygoteServer 的内部实现 位置:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

ini 复制代码
ZygoteServer(boolean isPrimaryZygote) {
    mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

    if (isPrimaryZygote) {
        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
        mUsapPoolSocket =
                Zygote.createManagedSocketFromInitSocket(
                        Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
    } else {
        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
        mUsapPoolSocket =
                Zygote.createManagedSocketFromInitSocket(
                        Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
    }

    mUsapPoolSupported = true;
    fetchUsapPoolPolicyProps();
}

通过上面可以看到在Zygote32Zygote64两个中分别创建不同的ZygoteSocket 下面看一下在Zygote中是如何创建Socket 的 代码位置:frameworks/base/core/java/com/android/internal/os/Zygote.java

java 复制代码
public static final String PRIMARY_SOCKET_NAME = "zygote";
public static final String SECONDARY_SOCKET_NAME = "zygote_secondary";
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";


static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
    int fileDesc;
    // 拼接 Socket 名称
    final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

    try {
        // 获取Socket 环境变量
        String env = System.getenv(fullSocketName);
        // 将环境变量转化为文件描述符参数
        fileDesc = Integer.parseInt(env);
    } catch (RuntimeException ex) {
        throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
    }

    try {
        FileDescriptor fd = new FileDescriptor();
        fd.setInt$(fileDesc);
        return new LocalServerSocket(fd);
    } catch (IOException ex) {
        throw new RuntimeException(
            "Error building socket from file descriptor: " + fileDesc, ex);
    }
}

上面代码后面可以看到使用获取到的fd创建了一个LocalServerSocket,也就是服务端的 Socket,并将这个文件操作符作为参数传进去。在 Zygote 进程将 SystemServer 进程启动后,就会在这个 服务器端的 Socket 上等待 AMS/ATMS 请求 Zygote 进程来创建新的应用程序进程。

下面看一下SystemServer是如何被启动的,上面代码中调用forkSystemServer函数创建,看一下具体实现:

scss 复制代码
private static Runnable forkSystemServer(String abiList, String socketName,
        ZygoteServer zygoteServer) {
    ....// 省略部分代码

    /* Hardcoded command line to start the system server */
    // 创建启动SystemServer 的参数
    String[] args = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
                    + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011,3012",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
            "com.android.server.SystemServer",
    };
    ZygoteArguments parsedArgs;

    int pid;

    try {
        ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);
        try {
            parsedArgs = ZygoteArguments.getInstance(commandBuffer);
        } catch (EOFException e) {
            throw new AssertionError("Unexpected argument error for forking system server", e);
        }
        commandBuffer.close();
        Zygote.applyDebuggerSystemProperty(parsedArgs);
        Zygote.applyInvokeWithSystemProperty(parsedArgs);

        ....// 省略部分代码

        /* Request to fork the system server process */
        // 创建一个System_server进程
        pid = Zygote.forkSystemServer(
                parsedArgs.mUid, parsedArgs.mGid,
                parsedArgs.mGids,
                parsedArgs.mRuntimeFlags,
                null,
                parsedArgs.mPermittedCapabilities,
                parsedArgs.mEffectiveCapabilities);
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }

    /* For child process */
    if (pid == 0) {
        if (hasSecondZygote(abiList)) {
            waitForSecondaryZygote(socketName);
        }

        zygoteServer.closeServerSocket();
        return handleSystemServerProcess(parsedArgs);
    }

    return null;
}

在创建Server可以看出 system_server 进程的用户 id 和用户组 id 被设置为 1000, 拥有用户组1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011,3012 的权限. 进程名为 system_server,启动的类名为 com.android.server.SystemServer,然后调用 Zygote.forkSystemServer函数创建一个进程

下面看一下 Zygote.forkSystemServer的实现:

arduino 复制代码
static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
        int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
    ZygoteHooks.preFork();
    // 调用native fork 一个进程
    int pid = nativeForkSystemServer(uid, gid, gids, runtimeFlags, rlimits,
            permittedCapabilities, effectiveCapabilities);

    // Set the Java Language thread priority to the default value for new apps.
    Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

    ZygoteHooks.postForkCommon();
    return pid;
}

nativeForkSystemServercom_android_internal_os_Zygote.cpp一个jni调用,简单看一下调用代码: 位置:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

arduino 复制代码
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
        JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
        jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
        jlong effective_capabilities) {
    ....// 省略部分代码

  pid_t pid = zygote::ForkCommon(env, true,
                                 fds_to_close,
                                 fds_to_ignore,
                                 true);
    ....// 省略部分代码
  return pid;
}

在看zygote::ForkCommon函数实现

c 复制代码
pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
                         const std::vector<int>& fds_to_close,
                         const std::vector<int>& fds_to_ignore,
                         bool is_priority_fork,
                         bool purge) {
    ....// 省略部分代码

  pid_t pid = fork();
     ....// 省略部分代码

  return pid;
}

到这里就可以看到native调用fork()函数克隆一个进程,这里介绍一下fork()返回pid的情况   1)在父进程中,fork返回新创建子进程的进程ID;   2)在子进程中,fork返回0;   3)如果出现错误,fork返回一个负值;

所以在上面ZygoteInit类中forkSystemServer函数中有这么一段代码:

scss 复制代码
/* For child process */
if (pid == 0) {
    if (hasSecondZygote(abiList)) {
        waitForSecondaryZygote(socketName);
    }

    zygoteServer.closeServerSocket();
    return handleSystemServerProcess(parsedArgs);
}

表示当pid为0时是运行在子进程中,也就是SystemServer进程中, handleSystemServerProcess函数完成系统服务剩余工作

scss 复制代码
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
    ....// 省略部分代码
    // 获取system server class path 
    final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
    if (systemServerClasspath != null) {
        performSystemServerDexOpt(systemServerClasspath);
        // Capturing profiles is only supported for debug or eng builds since selinux normally
        // prevents it.
        if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
            try {
                Log.d(TAG, "Preparing system server profile");
                prepareSystemServerProfile(systemServerClasspath);
            } catch (Exception e) {
                Log.wtf(TAG, "Failed to set up system server profile", e);
            }
        }
    }

    if (parsedArgs.mInvokeWith != null) {
        ....// 省略部分代码
        throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
    } else {
        // 创建类加载器
        ClassLoader cl = getOrCreateSystemServerClassLoader();
        if (cl != null) {
            Thread.currentThread().setContextClassLoader(cl);
        }

        return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                parsedArgs.mDisabledCompatChanges,
                parsedArgs.mRemainingArgs, cl);
    }
}

上面代码主要创建ClassLoader, 看一下ZygoteInit.zygoteInit函数, 其中环境变量SYSTEMSERVERCLASSPATH,配置如下 SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/com.android.location.provider.jar:/system/framework/service-jobscheduler.jar

scss 复制代码
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {
    if (RuntimeInit.DEBUG) {
        Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
    }

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
    RuntimeInit.redirectLogStreams();

    RuntimeInit.commonInit();
    ZygoteInit.nativeZygoteInit();
    return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
            classLoader);
}

RuntimeInit.applicationInit 位置:frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

scss 复制代码
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
        String[] argv, ClassLoader classLoader) {
    // If the application calls System.exit(), terminate the process
    // immediately without running any shutdown hooks.  It is not possible to
    // shutdown an Android application gracefully.  Among other things, the
    // Android runtime shutdown hooks close the Binder driver, which can cause
    // leftover running threads to crash before the process actually exits.
    nativeSetExitWithoutCleanup(true);

    VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
    VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);

    final Arguments args = new Arguments(argv);

    // The end of of the RuntimeInit event (see #zygoteInit).
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    // Remaining arguments are passed to the start class's static main
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

findStaticMain函数

php 复制代码
protected static Runnable findStaticMain(String className, String[] argv,
        ClassLoader classLoader) {
    Class<?> cl;

    try {
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException("Missing class when invoking static main " + className,ex);
    }

    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException( "Missing static main on " + className, ex);
    } catch (SecurityException ex) {
        throw new RuntimeException( "Problem getting static main on " + className, ex);
    }

    int modifiers = m.getModifiers();
    if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
        throw new RuntimeException("Main method is not public and static on " + className);
    }

    /*
     * This throw gets caught in ZygoteInit.main(), which responds
     * by invoking the exception's run() method. This arrangement
     * clears up all the stack frames that were required in setting
     * up the process.
     */
    return new MethodAndArgsCaller(m, argv);
}

上面代码主要是在ClassLoader中找到com.android.server.SystemServer类和main函数,最后通过MethodAndArgsCaller执行SystemServer, 最后看一下MethodAndArgsCaller的实现

php 复制代码
static class MethodAndArgsCaller implements Runnable {
    /** method to call */
    private final Method mMethod;

    /** argument array */
    private final String[] mArgs;

    public MethodAndArgsCaller(Method method, String[] args) {
        mMethod = method;
        mArgs = args;
    }

    public void run() {
        try {
            mMethod.invoke(null, new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
            Throwable cause = ex.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            } else if (cause instanceof Error) {
                throw (Error) cause;
            }
            throw new RuntimeException(ex);
        }
    }
}

到这里SystemServer便启动完成了

相关推荐
董可伦2 小时前
Dinky 安装部署并配置提交 Flink Yarn 任务
android·adb·flink
每次的天空2 小时前
Android学习总结之Glide自定义三级缓存(面试篇)
android·学习·glide
恋猫de小郭3 小时前
如何查看项目是否支持最新 Android 16K Page Size 一文汇总
android·开发语言·javascript·kotlin
flying robot4 小时前
小结:Android系统架构
android·系统架构
xiaogai_gai4 小时前
有效的聚水潭数据集成到MySQL案例
android·数据库·mysql
鹅鹅鹅呢5 小时前
mysql 登录报错:ERROR 1045(28000):Access denied for user ‘root‘@‘localhost‘ (using password Yes)
android·数据库·mysql
在人间负债^5 小时前
假装自己是个小白 ---- 重新认识MySQL
android·数据库·mysql
Unity官方开发者社区5 小时前
Android App View——团结引擎车机版实现安卓应用原生嵌入 3D 开发场景
android·3d·团结引擎1.5·团结引擎车机版
进击的CJR8 小时前
MySQL 8.0 OCP 英文题库解析(三)
android·mysql·开闭原则
Mckay8812 小时前
android studio导入项目
android·ide·android studio