news 2026/5/9 19:44:24

揭秘 Android Runtime:从代码到执行的魔法之旅

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
揭秘 Android Runtime:从代码到执行的魔法之旅

先把主题说透一点:

我们写的 Android App,大部分代码是 Java/Kotlin 写的,
但手机 CPU 只认识“机器指令(0 和 1)”。
中间那层“把 Java 世界变成 CPU 能跑的世界”的,
就是 Android Runtime:Dalvik / ART + 一堆核心类库

你可以把它想象成:

  • 一群会多国语言的翻译 + 管家:
    • 能听懂 Java/Kotlin(字节码)
    • 能跟底层的 C/C++ 库、Linux 内核沟通
    • 还负责内存管理、垃圾回收、线程调度、类加载等等

这篇就围绕几个问题展开:

  1. Android 为啥需要 Runtime,而不是直接“裸跑”Java?
  2. Dalvik 和 ART 有啥区别,为啥要从 Dalvik 换成 ART?
  3. Runtime 在架构里处于哪一层,和 App、Framework、Native 库是什么关系?
  4. 字节码是怎么跑起来的:解释执行、JIT、AOT 这些词背后都什么含义?
  5. GC(垃圾回收)、堆、栈、类加载、反射,这些经典概念在 ART 里是什么样?
  6. 核心类库(Core Libraries)包括啥?跟普通 JVM 有什么差别?
  7. 这些“底层机制”,对我们写 App 的性能、内存、启动速度有哪些具体影响?

一、把 Runtime 放进大地图:它在 Android 里是“哪一层”?

先简单画一张脑补图(文字版):

从下到上,Android 大致是:

  • Linux 内核(内存、进程、线程、文件系统、驱动、网络…)
  • Native 层(C/C++ 库:libc、OpenGL、libmedia、SQLite 等 + 各种 Native 服务进程)
  • Android Runtime(ART / Dalvik + Core Libraries)
  • Framework 层(Java/Kotlin 的系统服务 API:ActivityManager、View、Context 等)
  • App 层(你写的那些 APK)

Runtime 处在Java 世界Native 世界的夹缝位置:

  • 向“上”承接:

    • App 里的 Java/Kotlin 代码
    • Framework 里的大量 Java 代码(比如 Activity、View、Handler 等)
  • 向“下”接通:

    • 各种 C/C++ 库(通过 JNI)
    • Linux 提供的线程、内存、文件、Socket 等能力

你可以粗暴理解为:

Runtime = Java 虚拟机(简化版 JVM) + Android 自带的 Java 类库 + 一堆运行时管理组件(GC、线程、异常、类加载…)

没有它,Java/Kotlin 代码压根跑不起来。


二、为什么 Android 要有自己的 Runtime?不能直接用 JVM 吗?

很多人会问:

“Java 不是有 JVM 了吗?Android 为什么不直接把 JVM 搬进来?”

原因大致有几类:

2.1 授权 & 生态问题(历史原因)

早期 Java 的标准 JVM(Oracle 那套)有授权问题:

  • 商业使用要考虑版权、授权
  • Android 想做一个开放生态,不想被某家公司死死掐住脖子

于是 Google 选择:

  • 实现自己的一套 Java 字节码执行环境(Dalvik),
  • 使用的是 Java 语言,但运行时不完全等同于标准 JVM。

这也是当年 Google 和 Oracle 打官司的一个背景(不展开)。

2.2 移动设备的特点:内存小、电池有限、CPU 多样

桌面/服务器上的 JVM 没太把“省电、节省内存”当头号问题。
而手机上:

  • 内存少(尤其早期 Android 只有几百 MB)
  • CPU 弱,还可能是多种架构(ARM、x86、MIPS 等)
  • 电池续航是生死线

Runtime 的设计必须:

  • 更轻量
  • 更适合多进程、多 App 的场景
  • 更适应频繁创建/销毁进程、短生命周期程序

2.3 多 App、沙箱、多进程模型

Android 不是只跑一个大程序,而是:

  • 每个 App 是一个独立进程、有自己的虚拟机实例
  • 有各种安全隔离、权限控制

传统 JVM 往往是:

  • 在一个进程里跑一堆 Java 程序(比如 Web 容器里部署多个应用)

两种模式很不一样,Android Runtime 必须针对“一个进程一个虚拟机”的模式做专门优化。


三、Dalvik vs ART:Android Runtime 的两代“灵魂”

Android Runtime 的历史可以简单分两代:

  1. 第一代:Dalvik VM(早期 Android 到 4.x)
  2. 第二代:ART(Android Runtime)(从 5.0 开始逐渐替代)

3.1 Dalvik:一开始的“解释 + JIT”方案

Dalvik 的特点:

  • 使用的是自己的字节码格式:DEX(Dalvik Executable)
  • 设计为“寄存器架构”(register-based),而传统 JVM 是 stack-based
  • 早期主要靠解释执行,即一条一条翻译字节码给 CPU 跑
  • 后来逐步加入 JIT(Just-In-Time,即时编译),对热点代码做优化

简单类比:

解释执行:
字节码每执行一行,都临时翻译一次给 CPU,边翻译边跑。

JIT:
热门代码段,先“记下来”,然后一次性编译成机器码,下次就直接用。

Dalvik 在当年的手机环境下已经算不错了,但随着设备变强、App 越来越复杂,它的几大问题暴露出来:

  • 启动速度不够快
  • 运行效率不如原生机器码
  • GC 卡顿比较明显

于是 Google 搞了第二代 Runtime:ART。

3.2 ART:从“即时翻译”变为“提前编译为主”

ART(Android Runtime)做了一个很重要的改变:

把“安装应用时就编译(AOT, Ahead-of-Time)”作为基础策略,
加上运行时的 JIT 和 Profile 引导优化,
目标就是:

  • 启动更快
  • 运行更快
  • 整体更省电

它的大致思路是:

  1. 安装 APK 时,ART 把里面的 .dex 字节码编译成机器码(生成 .oat/.odex 等文件),存到本地。
  2. App 运行时,直接从这些编译好的机器码里调起,不再大量依赖解释执行。
  3. 运行中还有一套 JIT,它会记录哪些方法/代码块经常被执行,然后针对这些“热点”做进一步优化。

你可以把 Dalvik 和 ART 的差异理解为:

  • Dalvik:
    • 直接拿原文小说给你边翻译边读,翻译速度快但读起来不流畅。
  • ART:
    • 先把整部小说翻译成目标语言(安装时做),之后你随时看都是“本地语言版”,读起来流畅多了。

当然,Android 的实际实现比这个复杂得多,比如:

  • 存储空间考虑:不能把所有方法都编译成超大代码,否则浪费空间
  • 不同设备性能差异:低端设备可能不能放太多编译成果,策略要动态调整
  • 升级系统/更新应用时,还要考虑重新编译、兼容性等问题

四、从 APK 到执行:Runtime 眼里的一条完整链路

现在我们来走一遍“App 的 Java/Kotlin 代码是怎么跑起来的”。

4.1 开发期:Java/Kotlin → .class → .dex

你在 Android Studio 里写代码:

classMainActivity:AppCompatActivity(){overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}}

编译流程大致是:

  1. Kotlin/Java 源文件 → 编译成标准的 JVM.class字节码
  2. 编译工具再把一堆.class合并/转换成.dex格式(专门为 Dalvik/ART 设计)

所以 APK 里包含的是:

  • .dex(一个或多个):所有 Java/Kotlin 代码编译后的字节码
  • 资源文件、Manifest、native so 库等

4.2 安装期:.dex → 设备上的“可执行/优化格式”

在 Android 5.0 后,安装 APK 时:

  • ART 会扫描 .dex 文件
  • 根据当前设备的 CPU 架构(arm64, armv7, x86…)
  • 把字节码编译/优化成特定格式的文件(比如.oat/.vdex/.odex等)
  • 存到/data/dalvik-cache/或类似目录

这一步叫做“预编译 / 优化”(AOT & Dexopt)。

好处:

  • 下次运行 App 时,不需要从头解释执行 .dex
  • 冷启动速度明显提升

4.3 运行期:Zygote fork 出新进程,ART 开始干活

当你点开一个 App 图标时,系统会做这样几件事:

  1. 通过 Zygote 快速 fork 进程

    • Zygote 是一个“预热好了的 Java 虚拟机进程”,里面已经加载了核心类库和大部分 Framework 类
    • AMS(ActivityManagerService)让 Zygote fork 出一个子进程当成你的 App 进程
    • 子进程里已经有了 ART 运行时环境 + 大量预加载类(通过 COW 共享内存节省空间)
  2. 在新进程里加载你的应用代码

    • ART 在这个新进程里:
      • 设置好 ClassLoader
      • 加载你 APK 里的 .dex/.oat 文件
      • 初始化 Application 类、执行attachBaseContext()onCreate()
  3. 后续所有 Java/Kotlin 代码都跑在这个 ART 进程里

    • 当你调用框架 API,如new Thread {}ArrayList.add()View.draw()
    • 背后都是 ART 帮你执行指令、分配内存、处理异常/GC 等。

所以:

每个 App 进程里,都有一个 ART 运行时实例,
它就是你的 Java/Kotlin 世界的“宿主环境”。


五、解释执行 vs JIT vs AOT:三种“翻译模式”的区别

这是理解 Runtime 性能的关键。

5.1 解释执行:一步一翻译,执行速度慢但灵活

解释执行的过程类似于:

有一本外文书(字节码),
你不预先翻译,而是读一行翻一行,读一句翻一句。
所以你开始读很快(不需要准备),但整体读得不顺畅。

在 Dalvik 早期,这基本是主要方式:

  • 每条字节码都由解释器模块看一眼,然后根据指令含义执行对应 C 函数、汇编片段
  • 执行慢,但实现简单、占空间少

5.2 JIT(Just-In-Time,即时编译):对“热门段落”提前翻译

JIT 的思路:

你读书的时候发现某几个片段反复要看,那就干脆单独翻译好,之后再看就不用一遍遍现场翻译了。

在运行时:

  1. ART 统计哪些方法/代码段被频繁调用(热点),例如一个循环里的逻辑
  2. 当某个方法达到一定“热度阈值”,JIT 引擎就会把这一段字节码编译成机器码保存起来
  3. 下次再执行时,直接跳到编译好的机器码,速度大幅提升

优点:

  • 不用事先把所有代码都编译,只对“热点”优化
  • 节省空间
  • 能根据运行时实际情况优化(profile-guided)

5.3 AOT(Ahead-Of-Time,提前编译):安装时就大块翻译

AOT 的思路:

在你拿到外文书时,就让翻译提前把书翻成目标语言一遍,
之后你每次看都是本地语言版,读起来很快。

在 ART 的 AOT 模式下:

  • 安装 APK 时,就把绝大部分(或全部)字节码编译成本地机器码
  • 生成.oat/.odex等文件
  • 运行时直接调用机器码,解释执行大幅减少

优点:

  • 冷启动速度更快(初次执行不用 JIT 热身)
  • 运行效率更接近 C/C++ 的原生性能
  • 减轻运行时的 CPU 负担(对电池更友好)

缺点:

  • 安装时间变长
  • 生成的机器码占用磁盘空间
  • 不同 CPU 架构要分别编译

现实中,Android 采用的是AOT + JIT + Profile 混合策略

  • 初次安装时进行基础 AOT 编译
  • 运行中用 JIT 统计 profile(哪些代码经常跑)
  • 后台空闲或充电时,根据 profile 再做二次优化编译(profile-guided AOT)

六、GC(垃圾回收):ART 如何帮你“打扫内存卫生”

Java/Kotlin 世界的一个大卖点是:不用手动 free 内存,Runtime 会帮你做垃圾回收。

6.1 堆、栈、对象、引用:先建立直觉

在 ART 里,大致是这样:

  • 栈(Stack)

    • 存放方法调用栈帧、局部变量、方法参数
    • 每个线程一个栈
    • 方法结束后栈帧弹出,里面的局部变量自然消亡
  • 堆(Heap)

    • new出来的对象都在这里:new String()new ArrayList()
    • 多个线程共享
    • 对象什么时候被回收,不由你直接决定,而由垃圾回收算法判断
  • 引用关系

    • 只要还有某个“活动的”引用指向一个对象(可达),这个对象就不能被回收
    • 当一个对象不再能从任何“根对象”(线程栈、本地变量、静态变量等)触达时,它就成了垃圾

GC 的任务就是:

定期扫描堆,找出那些“再也用不到的对象”,把它们的内存回收掉。

6.2 ART 的 GC 策略:高效 + 尽量少卡顿

ART 使用了一系列 GC 算法组合,目标是在:

  • 不太浪费内存
  • 让应用尽量不卡顿(尤其是 UI 线程)
  • 支持多核并行、增量回收

你不用死记算法名字(Mark-Sweep、Mark-Compact、Concurrent GC、Generational GC 等),
了解几个关键点就够了:

  1. 分代回收

    • 年轻代 vs 老年代:
      • 新创建的对象放在“年轻代”区域,
      • 存活多次 GC 后才会晋升到“老年代”。
    • 因为绝大多数对象很快就不用了,把精力放在年轻代能更高效地回收垃圾。
  2. 并发标记 / 并发清理

    • GC 尽量在后台线程执行,减少停顿时间
    • 通过写屏障等机制,保证在标记过程中即便应用还在改引用关系,GC 也能正确判断
  3. Stop-The-World(STW)时间控制

    • 传统 GC 会有一段时间暂停所有应用线程(STW),
    • ART 努力把这段时间压缩到很短,降低卡顿感。

对我们来说,有几个结论很重要:

  • 你创建的每个对象都会增加 GC 压力。
  • 大量短生命的临时对象、频繁的分配与释放,会引发频繁 GC。
  • 当 GC 发生在主线程敏感时刻(比如滑动列表),就会出现丢帧、卡顿。

这就是为什么:

  • 避免在onDraw()onBindViewHolder()这样的热点函数里疯狂 new 对象
  • 避免创建过多临时字符串、盒装类型(如频繁装箱 Integer)
  • 尽量复用对象(比如 ViewHolder 模式)

这些都本质上是在“帮 ART 少干点 GC 的活”。


七、类加载 / 反射 / 多 Dex:Runtime 怎么管理你的代码世界?

Java 世界里有一个经典概念:ClassLoader(类加载器)

在 Android 中,这也是 Runtime 的一项重要工作。

7.1 ClassLoader:不同 APK 有自己的一套“类空间”

每个 App 进程里,ART 会创建一系列 ClassLoader:

  • 最底下:
    • BootClassLoader:加载核心类库(java.lang、java.util、android.* 等)
  • 上面:
    • PathClassLoader:加载应用自身的 classes.dex
  • 再上面可能还有:
    • DexClassLoader:你动态加载的 dex / jar

当你在代码中:

Class.forName("com.example.Foo");

ClassLoader 就会负责从对应的 dex 文件里找到这个类的定义,
如果没找到就抛ClassNotFoundException

好处:

  • 不同 App 的类互不干扰,即使有同名类也不会冲突
  • 系统可以控制哪些类对谁可见
  • 支持插件化(通过自定义 ClassLoader 动态加载新代码)

7.2 反射:Runtime 提供的“动态自省”能力

反射(Class,Method,Field等)也是 Runtime 的职责之一:

  • 管理类的元信息(字段、方法、注解等)
  • 支持运行时查找方法并调用

反射很强大,但也有成本:

  • 反射调用一般慢于直接方法调用
  • 需要额外保存元数据,占空间
  • 大量使用反射,还会加重 GC 压力

ART 对反射做了不少优化,但总体原则不变:

  • 反射用在框架和工具库上可以提高灵活性
  • 业务逻辑里大量反射容易伤性能

7.3 MultiDex:方法数多到爆,Runtime 也得适配

由于 Dalvik/ART 指令集限制,单个 dex 文件方法数有 65535 限制
大型 App(比如超级 App)很容易超过这个数,
于是就有了MultiDex

  • 把字节码拆成多个 dex 文件:classes.dex、classes2.dex、classes3.dex…
  • 启动时把多个 dex 文件都挂到 ClassLoader 的搜索路径里

Runtime 需要:

  • 支持多个 dex 文件同时被加载
  • 管理好它们之间的类索引和方法索引

MultiDex 会:

  • 增加启动成本(需要加载更多 dex 文件)
  • 影响内存使用

所以很多优化手段(比如代码分包、按需加载、R8/ProGuard 压缩)本质上也是在帮 Runtime 更轻松。


八、Core Libraries:Android 提供给 Java 世界的“标准工具箱”

Android Runtime 不只是一个“执行引擎”,还自带一整套 Java 类库。
这些就是所谓的Core Libraries(核心类库)

8.1 包括哪些?

大致可以分几类:

  1. Java 标准库的一部分:

    • java.lang.*

      • String、Object、Thread、Throwable、Class、Math…
    • java.util.*

      • List、Map、Set、HashMap、ArrayList、Collections、Date、Calendar…
    • java.io.*

      • InputStream、OutputStream、File、Reader、Writer…
    • java.net.*

      • URL、Socket、Http 等(早期)

    注意:Android 并不是完全实现所有标准 Java SE 的类库,而是选了一部分适合移动场景的子集。

  2. Android 专有库:

    • android.os.*

      • Binder、Handler、Looper、Message、Parcel…
    • android.util.*

      • Log、SparseArray 等
    • android.graphics.*

      • Canvas、Bitmap、Paint…
    • android.view.*

      • View、ViewGroup、MotionEvent…
    • android.content.*

      • Intent、Context、SharedPreferences…

    这些实际上属于 Framework 层的 Java API,但底层依托 Runtime 执行。

  3. 第三方兼容库(部分)

    • 比如部分 Apache Harmony 的实现(历史原因)
    • 后来引入了很多开源实现(如 OkHttp 做网络库的基础)

8.2 Core Libraries 与 ART 的关系

ART 负责:

  • 加载这些类(通过 BootClassLoader)
  • 存放它们的元数据、常量池
  • 提供执行环境(线程、栈、堆)

Core Libraries 就是我们日常写代码时用到的各种类,
而 Runtime 就是支撑这些类“活起来”的地基。


九、这些 Runtime 机制,具体会影响我们哪些开发体验?

说了这么多机制,你我最关心的还是:那对我写 App 有啥用?

我列几条很直接的影响点:

9.1 冷启动速度:AOT 编译和 Zygote 预加载

  • ART 通过安装时编译 + Zygote 预加载,
    大幅提升了 App 冷启动速度。

  • 对开发者来说:

    • 大量类、方法、字段,会影响 dex 大小和编译时间
    • 启动时大量初始化对象、静态字段,会增加 Runtime 压力,影响冷启动

结论:

  • 启动流程要精简:
    • 不要在 Application.onCreate() 里干太多活
    • 不要一次性初始化所有 SDK

9.2 内存与 GC 卡顿:对象分配模式要合理

  • ART 的 GC 虽然更先进,但卡顿问题永远不会“自动消失”
  • 高频 new 对象、过多临时对象、长生命周期大对象,都会给 Runtime 压力

建议:

  • 避免在 onDraw/onLayout/onBindViewHolder/热点循环里 new 大对象
  • 重用对象池(比方说 RecyclerView 的 ViewHolder、Bitmap 缓存等)
  • 使用合适的数据结构(比如 SparseArray 替代 HashMap<Integer,?>)

9.3 反射、动态代理、大量 Kotlin 特性:Runtime 也会累

  • 动态反射调用(如 Gson、各种 ORM、依赖注入框架大量使用反射)
  • Lambda、大量 inline 函数、高阶函数(Kotlin)
  • 这些都可能增加方法数、类数、元数据量,使 Runtime 的工作变重

办法:

  • 适当使用编译期代码生成(如 Dagger、kapt)减少运行时反射
  • 对性能敏感逻辑,避免过度函数式、过度抽象
  • 用 Profiler 和 Systrace 看清楚热点在哪

9.4 MultiDex:方法数爆炸对 Runtime 的影响

  • 方法数过多会触发 MultiDex,启动时需要加载多个 dex 文件
  • Runtime 在类加载、方法查找上工作量更大,可能影响启动和内存

解决办法:

  • 使用 R8/ProGuard 做混淆、压缩,去掉没用代码
  • 模块化拆分,避免一个主 dex 太大
  • 对大体量 App 做专项启动优化

十、最后用一段话,把 Android Runtime 这块“黑盒”打开

把上面所有内容浓缩成一个生活化比喻:

整个 Android 世界里,

  • Linux 内核 是土地和水电
  • C/C++ Native 库 是各种机器设备(锅炉、空调、网络交换机)
  • Framework API 是政府制定的规章制度和办事窗口
  • App 是各种住户和公司

那 **Android Runtime(ART / Dalvik + Core Libraries)**呢?

它是:

  • 一套专门给“说 Java/Kotlin 语言的人”准备的生活区;
  • 里面有翻译官(解释器、JIT、AOT),把 Java 字节码变成 CPU 指令;
  • 有垃圾清理工(GC),定期清理没人用的内存垃圾;
  • 有档案管理员(ClassLoader/反射),整理和查找所有的类和方法;
  • 有工具库仓库(Core Libraries),提供日常要用的各种工具(集合、IO、网络、线程等)。

你写的每一行 Java/Kotlin 代码,
能在哪个进程里活起来、什么时候被执行、什么时候被回收、
怎么跟底层 native 库说话,
都是通过这套 Runtime 来安排的。

理解 ART / Dalvik + 核心类库,并不是为了你以后自己去写个虚拟机,
而是当你面对“启动慢、卡顿、内存抖动、方法数超限、反射过多”等问题时,
脑子里有一张“这背后到底是谁在干活”的清晰地图,
能在正确的层级去优化和定位问题。

这才是理解 Android Runtime 的真正意义。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 2:36:12

传统VS现代:AI如何提升宠物App开发效率10倍

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 制作一个对比展示&#xff1a;1)传统方式开发懂撸帝宠物社交App的典型流程和时间估算&#xff1b;2)使用快马平台AI辅助开发的流程和时间节省。重点展示&#xff1a;用户系统自动生…

作者头像 李华
网站建设 2026/5/8 2:14:08

开源突破:WebRL-Llama-3.1-8B让AI网页智能体成功率提升8倍

开源突破&#xff1a;WebRL-Llama-3.1-8B让AI网页智能体成功率提升8倍 【免费下载链接】webrl-llama-3.1-8b 项目地址: https://ai.gitcode.com/zai-org/webrl-llama-3.1-8b 导语 智谱AI发布的WebRL-Llama-3.1-8B开源模型&#xff0c;通过创新强化学习技术将网页操作任…

作者头像 李华
网站建设 2026/5/9 0:36:25

1小时用MCP工具打造智能客服原型:实战演示

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个智能客服系统原型&#xff0c;要求&#xff1a;1. 基于NLP的意图识别 2. 多轮对话管理 3. 知识库检索 4. 工单生成 5. 满意度评价。使用MCP工具在1小时内完成可交互原型&am…

作者头像 李华
网站建设 2026/5/9 1:27:05

uni-data-select在电商筛选功能中的实战应用

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个电商商品筛选页面&#xff0c;使用uni-data-select实现以下功能&#xff1a;1.三级分类联动选择 2.品牌多选功能 3.价格区间选择 4.商品属性筛选。要求&#xff1a;1.分类数…

作者头像 李华
网站建设 2026/5/9 2:27:13

ERNIE 4.5:300B参数异构MoE模型如何重塑企业级AI应用格局

ERNIE 4.5&#xff1a;300B参数异构MoE模型如何重塑企业级AI应用格局 【免费下载链接】ERNIE-4.5-300B-A47B-FP8-Paddle 项目地址: https://ai.gitcode.com/hf_mirrors/baidu/ERNIE-4.5-300B-A47B-FP8-Paddle 导语 百度ERNIE 4.5系列大模型以300B总参数、47B激活参数的…

作者头像 李华
网站建设 2026/5/9 1:44:53

个人收款系统终极部署指南:零基础搭建高效支付解决方案

个人收款系统终极部署指南&#xff1a;零基础搭建高效支付解决方案 【免费下载链接】xpay Exrick/xpay 是一个用于集成多种支付方式的 SDK。适合在移动应用和网站中实现支付功能。特点是提供了丰富的支付方式、简洁易用的 API 和良好的兼容性。 项目地址: https://gitcode.co…

作者头像 李华