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便启动完成了

相关推荐
CL_IN2 小时前
高效集成销售订单数据到MySQL的方法
android·数据库·mysql
devlei3 小时前
Android JankStats实现解析
android
Vesper633 小时前
【Android】‘adb shell input text‘ 模拟器输入文本工具使用教程
android·adb
MyhEhud4 小时前
Kotlin apply 方法的用法和使用场景
android·kotlin·kotlin apply函数
Code额4 小时前
MySQL的事务机制
android·mysql·adb
蓝莓浆糊饼干6 小时前
请简述一下String、StringBuffer和“equals”与“==”、“hashCode”的区别和使用场景
android·java
李斯维8 小时前
深入理解 Android Canvas 变换:缩放、旋转、平移全解析(一)
android·canvas·图形学
_一条咸鱼_8 小时前
Android Retrofit 框架日志与错误处理模块深度剖析(七)
android
顾林海8 小时前
Flutter Dart 面向对象编程全面解析
android·前端·flutter
去伪存真8 小时前
摸着石头过河,重新支棱起Capacitor老项目
android·前端