Android---系统启动流程
目录
Android 系统启动流程
init 进程分析
init.rc 解析
Zygote 概叙
Zygote 触发过程
Zygote 启动过程
什么时Runtime?
System Server 启动流程
Fork 函数
总结
面试题
Android 是 google 公司开发的一款基于 Linux 的开源操作系统。
Android 系统启动流程
android 系统启动的大概流程如下图所示:

第一步:启动电源以及系统启动
当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到 RAM,然后执行。
第二步:引导程序
引导程序是在 Android 操作系统开始运行前的一个小程序。引导程序是运行的第一个程序,因此它是针对特定的主板与芯片的。设备制造商要么使用很受欢迎的引导程序比如 reboot, uboot, qibootloader 或者开发自己的引导程序,它不是 Android 操作系统的一部分。引导程序是 OEM 厂商或者运营商加锁和限制的地方。
引导程序分两个阶段执行:
第一阶段:检测外部的RAM以及加载第二阶段有用的程序。
第二阶段:引导程序设置网络、内存等等。这些对于运行内核是必须的,为了达到特殊的目标,引导程序可以根据配置参数或者输入数据设置内核。
Android 引导程序可以在 \bootable\bootloader\regacy\usbloader 找到。传统的加载器包含两个文件,需要在这里说明:
init.s 初始化堆栈,清零 BBS 段,调用 main.c 的 main() 函数;
main.c 初始化硬件(闹钟、主板、键盘、控制台) ,创建 linux 标签
第三步:内核(Kernel)
Android 内核与桌面 linux 内核启动方式差不多。内核启动时,设置缓存、保护存储器、计划列表、加载驱动。当内核完成系统设置,它首先在系统文件种寻找 "init" 文件,然后启动 root 进程或者系统的第一个进程。
第四步:init 进程
init 进程是 Linux 系统中用户空间的第一个进程,进程号固定为1。Kernel 启动后,在用户空间启动 init 进程,并调用 init 中的 main() 方法执行 init 进程的职责。
第五步:启动 Lancher App
init 进程分析
其中 init 进程是 Android 系统中及其重要的第一个进程,init 进程做了如下三件事:
1. 创建和挂载启动所需要的文件目录
2. 初始化和启动属性服务
3. 解析 init.rc 配置文件并启动 zygote 进程

init.rc 解析
init.rc 是一个非常重要的配置文件,它是由 Android 初始化语言(Android Init Language) 编写的脚本,它主要包含五种类型语句:Action(Action 中包含了一系列的 Command)、Commands(init 语言中的命令)、Services(由 init 进程启动的服务)、Options(对服务进行配置的选项) 和 Import (导入其它配置文件)。init.rc 的配置代码如下所示:
# \system\core\rootdir\init.rc
on init # L41
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom
...
on <trigger> [&& <trigger>]* //设置触发器
<command>
<command> //动作触发之后要执行的命令
service <name> <pathname> [ <argument> ]* //<service的名字><执行程序路径><传递参
数>
<option> //Options是Services的参数配置. 它们影响Service如何运行及运行时机
group <groupname> [ <groupname>\* ] //在启动Service前将group改为第一个
groupname,第一个groupname是必须有的,
//默认值为root(或许默认值是无),第二个groupname可以不设置,用于追加组(通过
setgroups)
priority <priority> //设置进程优先级. 在-20~19之间,默认值是0,能过
setpriority实现
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]//创建
一个unix域的socket,名字叫/dev/socket/name , 并将fd返回给Service. type 只能是
"dgram", "stream" or "seqpacket".
...
Action
Action: 通过触发器 trigger,即以 on 开头的语句来决定执行相应的 service 的时机:
on early-init: 在初始化早期阶段触发;
on init: 在初始化阶段触发;
on late-init: 在初始化晚期阶段触发;
on boot/charger: 当系统启动/充电时触发;
on property: 当属性值满足条件时触发。
Service
服务 Service,以 service 开头,由 init 进程启动,一般运行在 init 的一个子进程,所以启动 service 前需要判断对应的可执行文件是否存在。init 生成的子进程,定义在 rc 文件,其中每一个 service 在启动时会通过 fork 方式生成子进程。
例如:service servicemanager /system/bin/servicemanager 代表的是服务名为 servicemanager,服务执行的路径为 /system/bin/servicemanager。
Command
下面列举常用的命令:
class_start<service_class_name>: 启动属于同一个 class 的所有服务;
start<service_name>: 启动指定的服务,若已启动则跳过;
stop<service_name>: 停止正在运行的服务;
setprop: 设置属性值;
mkdir: 创建指定目录;
symlink<sym_link>: 创建连接到的<sym_link>符号链接;
write: 向文件 path 中写入字符串;
exec: fork并执行,会阻塞 init 进程直到程序完毕;
exprot: 设置环境变量;
loglevel: 设置 log 级别。
Options
Options 是 Service 的可选项,与 Service 配合使用
disabled: 不随 class 自动启动,只根据 service 名才启动;
oneshot: service 退出后不再重启;
user/group: 设置执行服务的用户/用户组,默认都是 root;
class: 设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为 default;
onrestart: 当服务重启时执行相应命令;
socket: 创建名为 /dev/socket/<name> 的socket
critical: 在规定时间内该 service 不断重启,则系统会重启并进入恢复模式。
default: 意味着 disabled = false, oneshot = false, critical = false。
Zygote 概叙
Zygote 中文翻译为“受精卵”,它主要用于孵化子进程。在 Android 系统中有以下两种程序:Java 应用程序,主要基于 ART(Android Runtime) 虚拟机,所有的应用程序 apk 都属于这类 native 程序,也就是利用 C/C++ 语言开发的程序,如 bootanimation。所有的 Java 应用程序进程及系统服务(SystemServer)进程都由 Zygote 进程通过 Linux 的 fork() 函数卵化出来的,也就是为什么把它称为 Zygote 的原因,因为他就像一个受精卵,卵化出无数子进程,而 native 程序则由 Init 程序创建启动。Zygote 进程最初的名字不是“zygote”而是“app_process”,这个名字是在 Android.mk 文件中定义的。
Zygote 是 Android 中的第一个 ART 虚拟机,他通过 socket 的方式与其它进程进行通信。这里的“其他进程”其实主要是系统进程--SystemServer。
Zygote 是一个 C/S 模型,Zygote 进程作为服务端,它主要负责创建 Java 虚拟机,加载系统资源,启动 SystemServer 进程,以及在后续运行过程中启动普通的应用程序,其他进程作为客户端向它发出“孵化”请求,而 Zygote 接收到这个请求后就“孵化”出一个新的进程。比如,当点击 Launcher 里的应用程序图标去启动一个新的应用程序时,这个请求会到达框架层的核心服务 ActivityManagerService 中,当 AMS 收到这个请求后,它通过调用 Process 类发出一个“孵化”子进程的 Socket 请求,而 Zygote 监听到这个请求后就立刻 fork 一个新的进程出来。
Zygote 触发过程
1. init.zygoteXX.rc
import /init.${ro.zygote}.rc
${ro.zygote} 会被替换成 ro.zygote 的属性值,这个是由不同的硬件厂商自己制定的,由四个值:
zygote32: zygote进程对应的执行程序是 app_process(纯32bit 模式)
zygote64: zygote进程对应的执行程序是 app_process64(纯32bit 模式)
zygote32_64: 启动两个 zygote 进程(名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process32(主模式)、approcess63
zygote64_32: 启动两个 zygote 进程(名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process64(主模式)、app_precess32
2. start zygote
位置:system\core\rootdir\init.rc,zygote-start 是在 on late-init 中触发的。
3. app_processXX
Zygote 启动过程

app_process 里面定义了三种应用程序类型:
1. Zygote: com.android.internal.os.ZygoteInit
2. SystemServer 不单独启动,而是由 Zygote 启动
3. 其他指定类名的 Java 程序
什么时Runtime?
Runtime 是支撑程序运行的基础库,它是与语言绑定在一起的。比如:
C Runtim: 就是 C standard lib,也就是我们常说的 libc。(有意思的是,Wiki 会自动将" C Runtime" 重定向到"C Standard Library")。
Java Runtime: 同样,Wiki 将其重定向到" Java Virtual Machine",这里当然包括 Java 的支撑类库(.jar)。
AndroidRuntime: 显而易见,就是为 Android 应用运行所需要的运行时环境。这个环境包括以下内容:
1. Dalvik VM: Android 的 Java VM, 解释运行 Dex 格式 Java 程序。每个进程运行一个虚拟机(什么叫运行虚拟机?说白了,就是一些 C 代码,不停的去解释 Dex 格式的二进制码(Bytecode),把它们转成机器码(Machine code),然后执行,当然,限制大多数的 Java 虚拟机都支持 JIT,也就是说,bytecode 可能在运行前就以及被转换成机器码,从而大大提高了性能。过去一个普遍的认识是 Java 程序比 C/C++ 等静态编译的语言慢,但随着 JIT 的介入和发展,这个有已经完全是过去式了,JIT 的动态性运行允许虚拟机根据运行时环境,优化机器码的生成,在某些情况下,Java 甚至可以比 C/C++ 跑得更快,同时又兼具平台无关性的特性。
2. Android 的 Java 类库,大部分来自于 Apache Hamony,开源的 Java API 实现,如 java.lang, java.util, java.net。但去除了 AWT,Swing 等部件。
3. JNI: C 和 Java 互掉的接口。
4. Libc: Android 也有很多 C 代码,自然少不了 libc,注意的是,Android 的 libc 叫 bionic C
// \frameworks\base\core\jni\androidRuntime.cpp start() L1091
void AndroidRuntime::start(const char* className, const Vector<String8>&
options, bool zygote)
{
...
JNIEnv* env;
//JNI_CreateJavaVM L1015
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
...
}
Java 虚拟机的启动大致做了以下一些事情:
1. 从 property 读取一系列启动参数。
2. 创建和初始化结构体全局对象(每个进程)GDVM,及对应于 JavaVM 和 JNIEnv 的内部结构体 JavaVMExt, JNIEnvExt.
3. 初始化 java 虚拟机,并创建虚拟机线程。
4. 注册系统的 JNI,Java 程序通过这些 JNI 接口来访问底层的资源。
loadJniLibrary("javacore");
loadJniLibrary("nativehelper");
5. 为 Zygote 的启动做最后的准备,包括设置 SID/UID,以及 mount 文件系统。
6. 返回 JavaVM 给 Native 代码,这样它就可以向上访问 Java 的接口。
System Server 启动流程
System Server 是 Zygote fork 的第一个 java 进程,这个进程非常重要,因为他们有很多的系统线程,提供所有核心的系统服务。
看到大名鼎鼎的 WindowManager,ActivityManager 了吗?对了,它们都是运行在 system_server 的进程里。还有很多 "Binder-x" 的线程,它们是各个 Service 为了响应应用程序远程调用请求而创建的。除此之外,还有很多内部线程,比如”UI thread", "InputReader", "InputDispatch" 等等,现在我们只关心 System Server 是如何创建起来的。
SystemServer 的 main() 函数。
public static void main(String[] args) {
new SystemServer().run();
}
接下来我们分成4部分详细分析 SystemServer.run 方法的初始化流程:
初始化必要的 SystemServer 环境参数,比如系统时间、默认时区,语言、load 一些 Library 等等。
初始化 Looper,我们在主线程中使用到的 looper 就是在 SystemServer 中进行初始化的。
初始化 Context,只有初始化一个 Context 才能进行启动 Service 等操作,这里看一下源码:
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);
final Context systemUiContext = activityThread.getSystemUiContext();
systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}
public ContextImpl getSystemContext() {synchronized (this) {if (mSystemContext == null) {mSystemContext = ContextImpl.createSystemContext(this);}return mSystemContext;}
}
ContextImpl 是 Context 类的具体实现,里面封装完成了几种常用的 createContext 的方法:
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
//省略代码
return context;
}
static ContextImpl createSystemUiContext(ContextImpl systemContext) {
final LoadedApk packageInfo = systemContext.mPackageInfo;
//省略代码
return context;
}
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk
packageInfo) {
if (packageInfo == null) throw new
IllegalArgumentException("packageInfo");
//省略代码
return context;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder
activityToken, int displayId,
Configuration overrideConfiguration) {
//省略代码
return context;
}
初始化 SystemServiceManager,用来管理启动 Service,SystemServiceManager 中封装了启动 Service 的 startService 方法启动系统必要的 Service,启动 service 的流程又分成三步:
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
//
} finally {
traceEnd();
}
启动 BootstrapServices,就是系统必须需要的服务,这些服务直接耦合性很高,所以就干脆放在一个方法里面一起启动,比如 PowerManagerService、RecoverySystemService、DisplayMangerService、ActivityManagerService 等等启动以基本的核心 Service,很简单,只有三个 BatteryService、UsageStatsService、WebViewUpdateService 启动其它需要用到的 Service 比如 NetworkScoreService、AlarmManagerService
Fork 函数
pid_t fork(void)

总结
1. init 根据 init.rc 运行 app_process,并携带“-zygote" 和"-startSystemServer"参数。
2. AndroidRuntime.cpp::start() 里将启动 JavaVM,并且注册所有 framework 相关的系统 JNI 接口
3. 第一次进入 Java 世界,运行 ZygoteInit.java::main() 函数初始化 Zygote,并创建 Socket 的 Server 端。
4. 然后 fork 一个新的进程并在新进程里初始化 SystemServer.Fork 之前,Zygote 是 preload 常用的 Java 类库,以及系统的 resource,同时 GC 清理内存空间,为子进程省去重复的工作。
5. SystemServer 将所有的系统 Service 初始化,包括 ActivityManager 和 WindowManager,他们是应用程序运行起来的前提。
6. 依次同时,Zygote 监听服务端 Socket,等待新的应用启动请求。
7. ActivityManager ready 之后寻找系统的 “Startup" Application,将请求发给 Zygote。
8. Zygote 收到请求后,fork 出一个新的进程。
9. Zygote 监听并处理 SystemServer 的 SIGCHID 信号,一旦 SystemServer 崩溃,立即将自己 kill 掉。init 会重启 Zygote。
面试题
相关文章:
Android---系统启动流程
目录 Android 系统启动流程 init 进程分析 init.rc 解析 Zygote 概叙 Zygote 触发过程 Zygote 启动过程 什么时Runtime? System Server 启动流程 Fork 函数 总结 面试题 Android 是 google 公司开发的一款基于 Linux 的开源操作系统。 Android 系统启动…...
【网络】http协议
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
Thread::interrupted() 什么意思? 如何中断线程?
1、答: Thread::interrupted() 是一个静态方法,用于判断当前线程是否被中断,并清除中断标志位。 具体来说,当一个线程被中断后,它的中断状态将被设置为 true。如果在接下来的某个时间点内调用了该线程的 interrupted…...
Oracle OCP 19c 考试(1Z0-083)中关于Oracle不完全恢复的考点(文末附录像)
欢迎试看博主的专著《MySQL 8.0运维与优化》 下面是Oracle 19c OCP考试(1Z0-083)中关于Oracle不完全恢复的题目: A database is configured in ARCHIVELOG mode A full RMAN backup exists but no control file backup to trace has been taken A media…...
一起来学习配置Combo接口吧!
Combo接口是一个光电复用的逻辑接口,一个Combo接口对应设备面板上一个GE电接口和一个GE光接口。电接口与其对应的光接口是光电复用关系,两者不能同时工作(当激活其中一个接口时,另一个接口就自动处于禁用状态)…...
C++模拟实现红黑树
目录 介绍----什么是红黑树 甲鱼的臀部----规定 分析思考 绘图解析代码实现 节点部分 插入部分分步解析 ●父亲在祖父的左,叔叔在祖父的右: ●父亲在祖父的右,叔叔在祖父的左: 测试部分 整体代码 介绍----什么是红黑树 红…...
HTTPS协议之SSL/TLS详解(下)
目录 前言: SSL/TLS详解 HTTP协议传输安全性分析 对称加密 非对称加密 证书 小结: 前言: 在网络世界中,存在着运营商劫持和一些黑客的攻击。如果明文传输数据是很危险的操作,因为我们不清楚中间传输过程中就被哪…...
OLE对象是什么?为什么要在CAD图形中插入OLE对象?
OLE对象是什么?OLE对象的意思是指对象连接与嵌入。那为什么要在CAD图形中插入OLE对象?一般情况下,在CAD图形中插入OLE对象,是为了将不同应用程序的数据合并到一个文档中。本节内容小编就来给大家分享一下在CAD图形中插入OLE对象的…...
【微信小程序】-- 自定义组件 -- 数据、方法和属性(三十三)
💌 所属专栏:【微信小程序开发教程】 😀 作 者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...
【Spring 深入学习】AOP的前世今生之代理模式
AOP的前世今生之代理模式1. 概述 什么是代理模式呢??? 在不修改原有代码 或是 无法修改原有代码的情况下,增强对象功能,替代原来的对象去完成功能,从而达成了拓展的目的。 先给大家看下 JavaScript中实现方…...
操作系统复试
2017软学 给出操作系统的定义,分别从资源管理,任务调度,用户接口等三个方面论述操作系统的职能 操作系统是位于硬件层之上、所有其他系统软件层之下的一个系统软件,使得管理系统中的各种软件和硬件资源得以充分利用,方…...
藏经阁(五)温湿度传感器 SHT3x-DIS 手册 解析
文章目录芯片特性芯片内部框图芯片引脚定义芯片温湿度范围芯片寄存器以及时序讲解信号转换公式芯片特性 湿度和温度传感器完全校准,线性化温度补偿数字输出供电电压范围宽,从2.4 V到5.5 VI2C接口通讯速度可达1MHz和两个用户可选地址典型精度 2% RH和 0.…...
PCB焊盘设计基本原则
SMT的组装质量与PCB焊盘设计有直接的关系,焊盘的大小比例十分重要。如果PCB焊盘设计正确,贴装时少量的歪斜可以再次回流焊纠正(称为自定位或自校正效应),相反,如果PCB焊盘设计不正确,即使贴装位置十分准确,…...
mysql锁分类大全
前言 为什么会出现锁 MySQL中的锁是为了保证并发操作的正确性和一致性而存在的。 当多个用户同时对同一份数据进行操作时,如果不加控制地进行读写操作,就可能导致数据不一致的问题。例如,当多个用户同时对同一行数据进行写操作时ÿ…...
推荐几款主流好用的远程终端连接管理软件
一、介绍 远程终端连接管理软件是管理服务器、虚拟机等远程计算机系统不可或缺的工具之一,它可以通过网络连接到另一台计算机,以执行命令、编辑文件或进行其他管理任务,下面我将为大家介绍几款主流好用的远程终端连接管理软件,并…...
描述性统计
参考文献 威廉 M 门登霍尔 《统计学》 文章目录定性数据的描述方法条形图饼图帕累托图定量数据点图茎叶图频数分布直方图MINITAB 工具在威廉《统计学》一书将统计学分为描述统计学和推断统计学,他们的定义分别如下:描述统计学:致力于数据集的…...
第十四届蓝桥杯三月真题刷题训练——第 7 天
目录 第 1 题:三角回文数 问题描述 答案提交 运行限制 代码: 第 2 题:数数 问题描述 答案提交 运行限制 代码: 第 3 题:倍数问题_同余定理_分情况讨论 题目描述 输入描述 输出描述 输入输出样例 运行限…...
剑指 Offer 57. 和为s的两个数字
一、题目 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。 示例 1: 输入:nums [2,7,11,15], target 9 输出:[2,7] 或者 [7…...
PDF转word在线转换方法!操作简单又高效
相信很多已经工作的人都知道,PDF文件格式的优点在于兼容性强、安全性高,而且查看和传输给他人都很方便。但是,这种格式的文件也有不太方便的地方,那就是不能对文件内容进行编辑和修改。对于许多人来说,如果想要编辑修改…...
Jquery项目中使用vue.js
大家在工作的情况中,可能会遇到之前的老项目采用jq书写,或者修改或者新增功能在jq中,原始jq的项目,代码可维护性很差,一个页面几千行jq,可维护性很差,工作量巨大,所以这个时候大家可以引入vue.js。 第一步:引入vue.js…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
