1. 字节码(Bytecode)是什么?

想象一下,你想让来自不同国家、说不同语言的人(比如英国人、法国人、中国人)都能理解你的指令。你有两种选择:

  • 选项 A(直接用机器语言): 你为英国人准备一份纯英文指令,为法国人准备一份纯法文指令,为中国人准备一份纯中文指令。这样每个人都能直接看懂,但你得准备很多份,而且每种语言都要精通,太麻烦了!这就像直接编写机器码 (Machine Code),不同类型的 CPU (手机芯片) 只能理解它们自己的特定机器码。
  • 选项 B(用中间语言): 你用一种设计好的、简单的“中间语言”(比如世界语 Esperanto,或者就叫“图画指令”)来写指令。这份指令本身可能英国人、法国人、中国人都看不懂。但是,你给每个人都配一个“翻译器”,英国人的翻译器能把“图画指令”翻译成英文,法国人的翻译器能翻译成法文,中国人的翻译器能翻译成中文。这样,你只需要准备一份“图画指令”,就能让所有人都最终理解你的意图。

字节码 (Bytecode) 就扮演着这种“中间语言”或“图画指令”的角色。

  • 它不是我们直接编写的 Java 或 Kotlin 代码(这是给人读的“源语言”)。
  • 它也不是 CPU 能直接执行的机器码(这是给机器读的“目标语言”)。
  • 它是 Java/Kotlin 代码经过编译器第一次处理后生成的一种平台无关的紧凑的指令格式。

2. DEX 字节码又是什么?

DEX (Dalvik Executable) 是 Android 平台特有的一种字节码格式。

  • 当你用 Java 或 Kotlin 编写 Android 应用时,编译工具首先会把它们变成标准的 Java 字节码(.class 文件)。
  • 然后,一个叫做 d8 (以前是 dx) 的工具会进一步把所有的 Java 字节码文件进行优化和转换,合并成一个或几个 .dex 文件
  • 这个 DEX 格式被特别设计用来适应手机这种内存和电量都比较受限的环境。它比标准的 Java 字节码更紧凑,读取效率也更高。

所以,DEX 就是 Android 世界里使用的那种特别优化的“图画指令”或“中间语言”。

为什么需要将代码从字节码(DEX)一次性翻译成机器能直接执行的本地机器码(通常是 OAT 文件)

原因很简单:手机的 CPU (中央处理器,也就是手机的“大脑”) 看不懂 DEX 字节码!

  • CPU 只能理解非常非常底层的机器码 (Machine Code)。不同的 CPU 型号(比如手机里常见的 ARM 架构 CPU,或者电脑里常见的 x86 架构 CPU)有它们自己专属的机器码指令集,就像它们只懂自己的“方言”。
  • DEX 字节码是一种通用的、“平台无关”的指令,它不属于任何一种特定 CPU 的“方言”。

所以,为了让 CPU 能够执行你的 App 代码,就必须有一个“翻译官”把通用的 DEX 字节码翻译成 CPU 能懂的本地机器码

这个“翻译官”就是我们之前提到的 ART (Android Runtime)

  • ART 的 AOT (预先编译):就像在 App 安装时,ART 这个翻译官就把整个 DEX 指令(“.dex”文件)提前翻译好,生成 CPU 能直接看的机器码(“.oat”文件)。以后运行时,CPU 直接看翻译好的就行了。
  • ART 的 JIT (即时编译):在某些情况下,ART 也可能扮演“同声传译”的角色,在代码即将运行时才把它从 DEX 翻译成机器码。

总结

  • DEX 字节码是一种为 Android 优化的、平台无关的“中间代码”。
  • 需要翻译是因为手机 CPU 只能理解自己特定的“方言”——机器码,而看不懂通用的 DEX 字节码。
  • ART 担当了这个翻译官的角色,负责将 DEX 字节码翻译成 CPU 可以执行的机器码,从而让你的 App 能够运行起来。

这种“编写一次(Java/Kotlin),到处运行(不同 Android 设备)”的模式,正是通过字节码和运行时的翻译机制实现的。