Android系统启动系列5 SystemServer进程下
发布日期:2021-05-06 20:19:12 浏览次数:10 分类:技术文章

本文共 19909 字,大约阅读时间需要 66 分钟。

一 概述

上一篇文章介绍了 SystemServer 的 main 函数是怎么被调用的,本篇主要分析 SystemServer 进程启动的时候,主要做了哪些事情,SystemServer 中运行的服务有八十多种,包括 ActivityManagerService(AMS),WindowManagerService(WMS),PackagManagerService(PMS)等,这些系统服务都是以一个线程的方式存在 Systemserver 进程中。接下来我们来看这些服务是怎么被启动的.

二 SystemServer启动流程

在这里插入图片描述

2.1 SystemServer.main

public static void main(String[] args) {       //new 一个SystemServer对象,再调用该对象的run()方法    new SystemServer().run();}

main 函数由 zygote 进程 fork 后运行,作用是 new 一个 SystemServer 对象,再调用该对象的 run() 方法.

2.2 SystemServer.run

private void run() {       try {           traceBeginAndSlog("InitBeforeStartServices");         // Record the process start information in sys props.        //从属性中读取system_server进程的一些信息        SystemProperties.set(SYSPROP_START_COUNT, String.valueOf(mStartCount));        SystemProperties.set(SYSPROP_START_ELAPSED, String.valueOf(mRuntimeStartElapsedTime));        SystemProperties.set(SYSPROP_START_UPTIME, String.valueOf(mRuntimeStartUptime));         EventLog.writeEvent(EventLogTags.SYSTEM_SERVER_START,                mStartCount, mRuntimeStartUptime, mRuntimeStartElapsedTime);         //如果一个设备的时钟是在1970年之前(0年之前),        //那么很多api 都会因为处理负数而崩溃,尤其是java.io.File#setLastModified        //我把把时间设置为1970        if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {               Slog.w(TAG, "System clock is before 1970; setting to 1970.");            SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);        }         //如果时区不存在,设置时区为GMT        String timezoneProperty = SystemProperties.get("persist.sys.timezone");        if (timezoneProperty == null || timezoneProperty.isEmpty()) {               Slog.w(TAG, "Timezone not set; setting to GMT.");            SystemProperties.set("persist.sys.timezone", "GMT");        }         //变更虚拟机的库文件,对于Android 10.0默认采用的是libart.so        SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());         // Mmmmmm... more memory!        //清除vm内存增长上限,由于启动过程需要较多的虚拟机内存空间        VMRuntime.getRuntime().clearGrowthLimit();		........        //系统服务器必须一直运行,所以它需要尽可能高效地使用内存        //设置内存的可能有效使用率为0.8        VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);         //一些设备依赖于运行时指纹生成,所以在进一步启动之前,请确保我们已经定义了它。        Build.ensureFingerprintProperty();         //访问环境变量前,需要明确地指定用户        //在system_server中,任何传入的包都应该被解除,以避免抛出BadParcelableException。        BaseBundle.setShouldDefuse(true);         //在system_server中,当打包异常时,信息需要包含堆栈跟踪        Parcel.setStackTraceParceling(true);         //确保当前系统进程的binder调用,总是运行在前台优先级(foreground priority)        BinderInternal.disableBackgroundScheduling(true);         //设置system_server中binder线程的最大数量,最大值为31        BinderInternal.setMaxThreads(sMaxBinderThreads);         //准备主线程lopper,即在当前线程运行        android.os.Process.setThreadPriority(                android.os.Process.THREAD_PRIORITY_FOREGROUND);        android.os.Process.setCanSelfBackground(false);        Looper.prepareMainLooper();        Looper.getMainLooper().setSlowLogThresholdMs(                SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);         //加载android_servers.so库,初始化native service        System.loadLibrary("android_servers");         // Debug builds - allow heap profiling.        //如果是Debug版本,允许堆内存分析        if (Build.IS_DEBUGGABLE) {               initZygoteChildHeapProfiling();        }         //检测上次关机过程是否失败,这个调用可能不会返回        performPendingShutdown();         //初始化系统上下文        createSystemContext();         //创建系统服务管理--SystemServiceManager        mSystemServiceManager = new SystemServiceManager(mSystemContext);        mSystemServiceManager.setStartInfo(mRuntimeRestart,                mRuntimeStartElapsedTime, mRuntimeStartUptime);        //将mSystemServiceManager添加到本地服务的成员sLocalServiceObjects        LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);        // Prepare the thread pool for init tasks that can be parallelized        //为可以并行化的init任务准备线程池        SystemServerInitThreadPool.get();    } finally {           traceEnd();  // InitBeforeStartServices    }    //启动服务    try {           traceBeginAndSlog("StartServices");        startBootstrapServices();   // 启动引导服务        startCoreServices();        // 启动核心服务        startOtherServices();       // 启动其他服务        SystemServerInitThreadPool.shutdown(); //停止线程池    } catch (Throwable ex) {           Slog.e("System", "******************************************");        Slog.e("System", "************ Failure starting system services", ex);        throw ex;    } finally {           traceEnd();    }     //为当前的虚拟机初始化VmPolicy    StrictMode.initVmDefaults(null);	........    //死循环执行    Looper.loop();    throw new RuntimeException("Main thread loop unexpectedly exited");}

先初始化一些系统变量,加载类库,创建 Context 对象,创建 SystemServiceManager 对象等候再启动服务,启动引导服务、核心服务和其他服务

2.3 SystemServer.performPendingShutdown

private void performPendingShutdown() {       final String shutdownAction = SystemProperties.get(            ShutdownThread.SHUTDOWN_ACTION_PROPERTY, "");    if (shutdownAction != null && shutdownAction.length() > 0) {           boolean reboot = (shutdownAction.charAt(0) == '1');         final String reason;        if (shutdownAction.length() > 1) {               reason = shutdownAction.substring(1, shutdownAction.length());        } else {               reason = null;        }         //如果需要重新启动才能应用更新,一定要确保uncrypt在需要时正确执行。        //如果'/cache/recovery/block.map'还没有创建,停止重新启动,它肯定会失败,        //并有机会捕获一个bugreport时,这仍然是可行的。        if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {               File packageFile = new File(UNCRYPT_PACKAGE_FILE);            if (packageFile.exists()) {                   String filename = null;                try {                       filename = FileUtils.readTextFile(packageFile, 0, null);                } catch (IOException e) {                       Slog.e(TAG, "Error reading uncrypt package file", e);                }                 if (filename != null && filename.startsWith("/data")) {                       if (!new File(BLOCK_MAP_FILE).exists()) {                           Slog.e(TAG, "Can't find block map file, uncrypt failed or " +                                "unexpected runtime restart?");                        return;                    }                }            }        }        Runnable runnable = new Runnable() {               @Override            public void run() {                   synchronized (this) {                       //当属性sys.shutdown.requested的值为1时,会重启                    //当属性的值不为空,且不为1时,会关机                    ShutdownThread.rebootOrShutdown(null, reboot, reason);                }            }        };         // ShutdownThread must run on a looper capable of displaying the UI.        //ShutdownThread必须在一个能够显示UI的looper上运行        //即UI线程启动ShutdownThread的rebootOrShutdown        Message msg = Message.obtain(UiThread.getHandler(), runnable);        msg.setAsynchronous(true);        UiThread.getHandler().sendMessage(msg);     }}

检测上次关机过程是否失败,这个调用可能不会返回

2.4 SystemServer.createSystemContext

private void createSystemContext() {       //创建system_server进程的上下文信息    ActivityThread activityThread = ActivityThread.systemMain();    mSystemContext = activityThread.getSystemContext();    //设置主题    mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);     //获取systemui上下文信息,并设置主题    final Context systemUiContext = activityThread.getSystemUiContext();    systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);}

初始化系统上下文, 该过程会创建对象有 ActivityThread,Instrumentation,ContextImpl,LoadedApk,Application

2.5 SystemServer.startBootstrapServices

private void startBootstrapServices() {       traceBeginAndSlog("StartWatchdog");    //启动watchdog    //尽早启动watchdog,如果在早起启动时发生死锁,我们可以让system_server    //崩溃,从而进行详细分析    final Watchdog watchdog = Watchdog.getInstance();    watchdog.start();    traceEnd();     ........    //添加PLATFORM_COMPAT_SERVICE,Platform compat服务被ActivityManagerService、PackageManagerService    //以及将来可能出现的其他服务使用。    traceBeginAndSlog("PlatformCompat");    ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,            new PlatformCompat(mSystemContext));    traceEnd();     //阻塞等待installd完成启动,以便有机会创建具有适当权限的关键目录,如/data/user。    //我们需要在初始化其他服务之前完成此任务。    traceBeginAndSlog("StartInstaller");    Installer installer = mSystemServiceManager.startService(Installer.class);    traceEnd();    ........    //启动服务ActivityManagerService,并为其设置mSystemServiceManager和installer    traceBeginAndSlog("StartActivityManager");    ActivityTaskManagerService atm = mSystemServiceManager.startService(            ActivityTaskManagerService.Lifecycle.class).getService();    mActivityManagerService = ActivityManagerService.Lifecycle.startService(mSystemServiceManager, atm);    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);    mActivityManagerService.setInstaller(installer);    mWindowManagerGlobalLock = atm.getGlobalLock();    traceEnd();     //启动服务PowerManagerService    //Power manager需要尽早启动,因为其他服务需要它。    //本机守护进程可能正在监视它的注册,    //因此它必须准备好立即处理传入的绑定器调用(包括能够验证这些调用的权限)    traceBeginAndSlog("StartPowerManager");    mPowerManagerService = mSystemServiceManager.startService(    PowerManagerService.class);    traceEnd();    ........    //初始化power management    traceBeginAndSlog("InitPowerManagement");    mActivityManagerService.initPowerManagement();    traceEnd();     //启动recovery system,以防需要重新启动    traceBeginAndSlog("StartRecoverySystemService");    mSystemServiceManager.startService(RecoverySystemService.class);    traceEnd();    ........    //启动服务LightsService    //管理led和显示背光,所以我们需要它来打开显示    traceBeginAndSlog("StartLightsService");    mSystemServiceManager.startService(LightsService.class);    traceEnd();    ........    //启动服务DisplayManagerService    //显示管理器需要在包管理器之前提供显示指标    traceBeginAndSlog("StartDisplayManager");    mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);    traceEnd();     // Boot Phases: Phase100: 在初始化package manager之前,需要默认的显示.    traceBeginAndSlog("WaitForDisplay");    mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);    traceEnd();     //当设备正在加密时,仅运行核心    String cryptState = VoldProperties.decrypt().orElse("");    if (ENCRYPTING_STATE.equals(cryptState)) {           Slog.w(TAG, "Detected encryption in progress - only parsing core apps");        mOnlyCore = true;    } else if (ENCRYPTED_STATE.equals(cryptState)) {           Slog.w(TAG, "Device encrypted - only parsing core apps");        mOnlyCore = true;    }    ........    //启动服务PackageManagerService    traceBeginAndSlog("StartPackageManagerService");    try {           Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);    } finally {           Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");    }    ........    //启动服务UserManagerService,新建目录/data/user/    traceBeginAndSlog("StartUserManagerService");    mSystemServiceManager.startService(UserManagerService.LifeCycle.class);    traceEnd();     // Set up the Application instance for the system process and get  started.    //为系统进程设置应用程序实例并开始。    //设置AMS    traceBeginAndSlog("SetSystemProcess");    mActivityManagerService.setSystemProcess();    traceEnd();     //使用一个ActivityManager实例完成watchdog设置并监听重启,    //只有在ActivityManagerService作为一个系统进程正确启动后才能这样做    traceBeginAndSlog("InitWatchdog");    watchdog.init(mSystemContext, mActivityManagerService);    traceEnd();     //传感器服务需要访问包管理器服务、app ops服务和权限服务,    //因此我们在它们之后启动它。    //在单独的线程中启动传感器服务。在使用它之前应该检查完成情况。    mSensorServiceStart = SystemServerInitThreadPool.get().submit(() -> {           TimingsTraceLog traceLog = new TimingsTraceLog(                SYSTEM_SERVER_TIMING_ASYNC_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);        traceLog.traceBegin(START_SENSOR_SERVICE);        startSensorService(); //启动传感器服务        traceLog.traceEnd();    }, START_SENSOR_SERVICE);}

用于启动系统 Boot 级服务,有 ActivityManagerService,PowerManagerService,LightsService,DisplayManagerService,PackageManagerService, UserManagerService, sensor 等服务.

2.6 SystemServer.startCoreServices

private void startCoreServices() {       //启动服务BatteryService,用于统计电池电量,需要LightService.    mSystemServiceManager.startService(BatteryService.class);     //启动服务UsageStatsService,用于统计应用使用情况    mSystemServiceManager.startService(UsageStatsService.class);    mActivityManagerService.setUsageStatsManager(            LocalServices.getService(UsageStatsManagerInternal.class));     //启动服务WebViewUpdateService    //跟踪可更新的WebView是否处于就绪状态,并监视更新安装    if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {           mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);    }     //启动CachedDeviceStateService,跟踪和缓存设备状态    mSystemServiceManager.startService(CachedDeviceStateService.class);     //启动BinderCallsStatsService, 跟踪在绑定器调用中花费的cpu时间    traceBeginAndSlog("StartBinderCallsStatsService");    mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);    traceEnd();     //启动LooperStatsService,跟踪处理程序中处理消息所花费的时间。    traceBeginAndSlog("StartLooperStatsService");    mSystemServiceManager.startService(LooperStatsService.Lifecycle.class);    traceEnd();     //启动RollbackManagerService,管理apk回滚    mSystemServiceManager.startService(RollbackManagerService.class);     //启动BugreportManagerService,捕获bugreports的服务    mSystemServiceManager.startService(BugreportManagerService.class);     //启动GpuService,为GPU和GPU驱动程序提供服务。    mSystemServiceManager.startService(GpuService.class);}

启动核心服务 BatteryService,UsageStatsService,WebViewUpdateService、BugreportManagerService、GpuService 等

2.7 SystemServer.startOtherServices

private void startOtherServices() {   	........    //启动TelecomLoaderService,通话相关核心服务    mSystemServiceManager.startService(TelecomLoaderService.class);     //启动TelephonyRegistry    telephonyRegistry = new TelephonyRegistry(context);    ServiceManager.addService("telephony.registry", telephonyRegistry);	........	//启动AlarmManagerService,时钟管理	mSystemServiceManager.startService(new AlarmManagerService(context));	........	//启动InputManagerService	inputManager = new InputManagerService(context);	ServiceManager.addService(Context.INPUT_SERVICE, inputManager,            /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);	........	inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());    inputManager.start();	........	//Phase480:在接收到此启动阶段后,服务可以获得锁设置数据    mSystemServiceManager.startBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);     //Phase500:在接收到这个启动阶段之后,服务可以安全地调用核心系统服务,    //如PowerManager或PackageManager。    mSystemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);		mActivityManagerService.systemReady(() -> {           //Phase550:在接收到此引导阶段后,服务可以广播意图。        mSystemServiceManager.startBootPhase(                SystemService.PHASE_ACTIVITY_MANAGER_READY); 		//Phase600:在接收到这个启动阶段后,服务可以启动/绑定到第三方应用程序。        //此时,应用程序将能够对服务进行绑定调用。        mSystemServiceManager.startBootPhase(        SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);	}}

启动其他的服务,开始处理一大堆尚未重构和整理的东西,这里的服务太多,大体启动过程类似,就不详细说明

三 服务启动分析

服务启动流程如下,从阶段0到阶段1000,一共8个阶段。

在这里插入图片描述

其中PHASE_BOOT_COMPLETED=1000,该阶段是发生在Boot完成和home应用启动完毕。系统服务更倾向于监听该阶段,而不是注册广播ACTION_BOOT_COMPLETED,从而降低系统延迟。

3.1 Phase0

startBootstrapServices() 启动引导级服务,主要启动以下10个服务:

Installer
DeviceIdentifiersPolicyService
UriGrantsManagerService
ActivityTaskManagerService
ActivityManagerService
PowerManagerService
ThermalManagerService
RecoverySystemService
LightsService
DisplayManagerService
启动完后,进入PHASE_WAIT_FOR_DEFAULT_DISPLAY=100, 即Phase100阶段

........    //1.启动DeviceIdentifiersPolicyService    mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);     //2.启动UriGrantsManagerService    mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);     //3.启动ActivityTaskManagerService    atm = mSystemServiceManager.startService(                ActivityTaskManagerService.Lifecycle.class).getService();     //4.启动PowerManagerService    mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);     //5.启动ThermalManagerService    mSystemServiceManager.startService(ThermalManagerService.class);     //6.启动RecoverySystemService    mSystemServiceManager.startService(RecoverySystemService.class);     //7.启动LightsService    mSystemServiceManager.startService(LightsService.class);     //8.启动DisplayManagerService    mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);        //执行回调函数 onBootPhase,把PHASE_WAIT_FOR_DEFAULT_DISPLAY=100, 传入各个service的 onBootPhase    mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);    ........}

3.2 Phase100

进入阶段 PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100 回调服务,启动阶段-Boot Phase,该阶段需要等待 Display 有默认显示,startBootPhase(100) -> onBootPhase(100).

从以下源码可以看到这里遍历了一下服务列表,然后回调到各服务的 onBootPhase() 方法中了。每个服务的 onBootPhase() 处理都不相同,这里不详细分析

public void startBootPhase(final int phase) {           ........        mCurrentPhase = phase;        ........        final int serviceLen = mServices.size();        for (int i = 0; i < serviceLen; i++) {               final SystemService service = mServices.get(i);            ........            try {                   service.onBootPhase(mCurrentPhase); // 轮训前面加过的service,把phase加入服务回调            } catch (Exception ex) {                   ........            }            ........        }        ........    }

创建以下80多个服务:

  • BatteryService
  • UsageStatsService
  • WebViewUpdateService
  • CachedDeviceStateService
  • BinderCallsStatsService
  • LooperStatsService
  • RollbackManagerService
  • BugreportManagerService
  • GpuService

3.3 Phase480

进入阶段 PHASE_LOCK_SETTINGS_READY=480 回调服务,该阶段后, 服务可以获取到锁屏设置的数据了,480到500之间没有任何操作,直接进入500.

3.4 Phase500

PHASE_SYSTEM_SERVICES_READY=500,进入该阶段服务能安全地调用核心系统服务,该阶段后,服务可以安全地调用核心系统服务,比如 PowerManager 或 PackageManager.

启动以下两个服务:
PermissionPolicyService
eviceSpecificServices

3.5 Phase520

PHASE_DEVICE_SPECIFIC_SERVICES_READY = 520,在接收到这个引导阶段之后,服务可以安全地调用特定于设备的服务。告诉AMS可以运行第三方代码,Making services ready,mActivityManagerService.systemReady()

3.6 Phase550

PHASE_ACTIVITY_MANAGER_READY = 550,该阶段后,服务可以接收到广播 Intents,AMS 启动 native crash 监控,启动 SystemUI,其余服务调用 systemReady()

  • AMS 启动native crash 监控
mActivityManagerService.startObservingNativeCrashes();
  • 启动 systemUI
    startSystemUi()
  • 其余服务调用 systemReady()
    networkManagementF.systemReady(),ipSecServiceF.systemReady(),networkStatsF.systemReady(),
    connectivityF.systemReady(),networkPolicyF.systemReady(networkPolicyInitReadySignal)

3.7 Phase600

PHASE_THIRD_PARTY_APPS_CAN_START = 600,该阶段后,服务可以启动/绑定到第三方应用程序。此时,应用程序将能够对服务进行绑定调用,各种服务调用systemRunning方法:locationF.systemRunning(),countryDetectorF.systemRunning(),networkTimeUpdaterF.systemRunning(),inputManagerF.systemRunning()......

3.8 Phase1000

PHASE_BOOT_COMPLETED = 1000,该阶段后,服务可以允许用户与设备交互。此阶段在引导完成且主应用程序启动时发生。系统服务可能更倾向于监听此阶段,而不是为完成的操作注册广播接收器,以减少总体延迟。在经过一系列流程,再调用 AMS.finishBooting() 时,则进入阶段Phase1000.到此,系统服务启动阶段完成就绪,system_server 进程启动完成则进入 Looper.loop() 状态,随时待命,等待消息队列 MessageQueue 中的消息到来,则马上进入执行状态

四 服务分类

system_server 进程启动的服务,从源码角度划分为引导服务、核心服务和其他服务三大类

  • 引导服务 Boot Service (10个):Installer,DeviceIdentifiersPolicyService,UriGrantsManagerService,ActivityTaskManagerService,ActivityManagerService,PowerManagerService,ThermalManagerService,RecoverySystemService,LightsService,DisplayManagerService
  • 核心服务 Core Service(9个):BatteryService、UsageStatsService、WebViewUpdateService,CachedDeviceStateService,BinderCallsStatsService,LooperStatsService,RollbackManagerService,BugreportManagerService,GpuService
  • 其他服务 Other Service(70个+)
    在这里插入图片描述

五 总结

  • zygote 启动后 fork 的第一个进程为 SystemServer,进程别名为 “system_server”,主要用来启动系统中的服务
  • zygote fork 后,进入 SystemServer 的 main()
  • SystemServer 在启动过程中,先初始化一些系统变量,加载类库,创建 Context 对象,创建 SystemServiceManager 对象等候再启动服务
  • 启动的服务分为 引导服务(Boot Service)、核心服务(Core Service)和其他服务(Other Service)三大类,共90多个服务
  • 启动的服务都单独运行在 SystemServer 的各自线程中,同属于 SystemServer 进程
上一篇:Android系统启动系列6 AMS的启动
下一篇:Android系统启动系列4 SystemServer进程上

发表评论

最新留言

感谢大佬
[***.8.128.20]2025年03月27日 00时18分01秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

【最短路】P4408 [NOI2003]逃学的小孩 2019-03-03
2020C证(安全员)模拟考试题及C证(安全员)模拟考试系统 2019-03-03
2020电工(初级)考试及电工(初级)考试软件 2019-03-03
2020N1叉车司机模拟考试题库及N1叉车司机复审模拟考试 2019-03-03
2020年G3锅炉水处理报名考试及G3锅炉水处理考试申请表 2019-03-03
2020年制冷与空调设备运行操作答案解析及制冷与空调设备运行操作考试总结 2019-03-03
2020年保育员(初级)考试资料及保育员(初级)新版试题 2019-03-03
2020年茶艺师(高级)考试内容及茶艺师(高级)考试申请表 2019-03-03
2021年过氧化工艺试题及答案及过氧化工艺考试平台 2019-03-03
2021年重氮化工艺考试题库及重氮化工艺考试报名 2019-03-03
2021年车工(高级)考试总结及车工(高级)试题及答案 2019-03-03
2021年压力焊证考试及压力焊实操考试视频 2019-03-03
2021年低压电工考试及低压电工考试申请表 2019-03-03
2021年低压电工考试及低压电工考试申请表 2019-03-03
2021年A特种设备相关管理(电梯)考试APP及A特种设备相关管理(电梯)复审考试 2019-03-03
2021年N1叉车司机考试题及N1叉车司机复审模拟考试 2019-03-03
2021年危险化学品经营单位主要负责人考试APP及危险化学品经营单位主要负责人多少钱 2019-03-03
2021年T电梯修理考试技巧及T电梯修理模拟考试软件 2019-03-03
2021年电工(初级)考试及电工(初级)证考试 2019-03-03
大数据学习之Spark——00Spark项目的pom.xml文件 2019-03-03