1. 名词解释
1.1 NDK(Native Development Kit)
全称:Android Native Development Kit,Android 原生开发工具包。
作用 :提供一整套交叉编译工具链(编译器、链接器、头文件、标准库等),让你能在电脑上把 C/C++ 代码编译成 Android 手机上运行的 .so 库。
关键历史:
- NDK r18(2018 年):移除 GCC,Clang 成为唯一编译器
- NDK r21(2020 年):停止支持 32 位 Windows
- NDK r23(2021 年):首次支持 Mac Apple Silicon(M1/M2)通用二进制
- NDK r24(2022 年):完整支持 Mac Apple Silicon
- NDK r25+(2022 年至今):稳定支持所有主流平台
本项目使用的 NDK:
- 项目未显式指定
ndkVersion,Gradle 自动选择 - Mac 本机已安装:21.1.6352462 / 23.1.7779620 / 24.0.8215888 / 28.2.13676358 / 30.0.14904198
- 推荐统一指定版本:
24.0.8215888(Mac M 系列 + Windows 均兼容)
1.2 CMake
是什么:一个跨平台的构建系统(构建工具)。
作用 :读取项目中的 CMakeLists.txt 配置文件,根据配置调度编译器来编译源码、链接库、生成最终产物。
类比:
CMake = 施工指挥(读图纸,安排工序)
NDK 工具链 = 施工工具(编译器、链接器)
源码 = 建筑材料(.c/.cpp 文件)
.so 库 = 建好的房子(编译产物)
关键配置:
cmake
# CMakeLists.txt 第一行,声明所需的最低 CMake 版本
cmake_minimum_required(VERSION 3.10.2)
- 如果本机 CMake 版本 ≥ 3.10.2 → 正常构建
- 如果本机 CMake 版本 < 3.10.2 → 报错
Android Studio 内置 CMake 版本:
| Android Studio 版本 | 内置 CMake | 说明 |
|---|---|---|
| 3.6 ~ 4.x | 3.10.2 | 老项目常用 |
| Arctic Fox+ | 3.22.1 | 较新版本 |
版本兼容建议:NDK r21+ 配 CMake 3.10.2 没问题,NDK r25+ 建议配 CMake 3.22+。
1.3 Clang
是什么:C/C++/Objective-C 编译器前端,是 LLVM 编译器基础设施的一部分。
名字由来 :C + lang(language)→ C 语言编译器前端。虽然名字里有"C",但实际支持 C、C++、Objective-C、Objective-C++ 等多种语言。
完整翻译 :无官方中文译名,直接使用 Clang。可理解为 "LLVM 的 C 语言前端"(C Language Frontend for LLVM)。
编译流程:
C/C++ 源码
↓
Clang(编译器前端)→ 词法分析、语法分析、语义分析 → 生成 LLVM 中间代码(IR)
↓
LLVM(编译器后端)→ 优化 → 生成目标平台机器码(如 ARM64)
↓
产出 .so 库
与 NDK 的关系 :从 NDK r18 起,GCC 被移除,Clang 成为 NDK 唯一的编译器。
1.4 LLVM
是什么:一个编译器基础设施项目,Clang 是它的前端。
分工:
- Clang(前端):负责把源码解析成中间代码 IR
- LLVM(后端):负责优化 IR,生成目标平台的机器码
优势:前端和后端解耦,同一套前端可以支持多个目标平台(ARM、x86、RISC-V 等),同一套后端也可以接收不同语言的中间代码。
1.5 交叉编译(Cross Compilation)
定义:在 A 平台上编译出 B 平台运行的程序。
典型场景:
开发电脑(Mac x86_64 / ARM64)
↓ 交叉编译
Android 手机(ARM64)上运行的 .so 库
为什么需要交叉编译:
- 手机上没有编译环境(没有编译器、构建工具等)
- 手机性能不足,不适合编译
- 所以在电脑上编译好,把结果(.so)打包进 APK,再部署到手机运行
对比:
| 类型 | 编译器运行平台 | 产物运行平台 | 举例 |
|---|---|---|---|
| 本地编译 | Mac | Mac | Mac 上跑的 .app |
| 交叉编译 | Mac | Android | Android 上跑的 .so |
NDK 就是 Android 的交叉编译工具包------提供运行在 Mac/Windows/Linux 上、但产出 Android 平台二进制的编译器(Clang)和工具链。
1.6 ABI(Application Binary Interface)
是什么:应用程序二进制接口,定义了二进制代码在特定 CPU 架构上运行的规范。
Android 支持的 ABI:
| ABI | CPU 架构 | 说明 |
|---|---|---|
| armeabi-v7a | ARM 32 位 | 老设备,主流兼容 |
| arm64-v8a | ARM 64 位 | 现代手机主流 |
| x86 | Intel/AMD 32 位 | 模拟器 |
| x86_64 | Intel/AMD 64 位 | 模拟器 |
与交叉编译的关系:交叉编译时需要指定目标 ABI,Clang 会根据 ABI 生成对应架构的机器码。
1.7 .so 文件(Shared Library)
是什么:共享库(动态链接库),C/C++ 编译后的最终产物。
在 Android 中的位置 :打包进 APK 的 lib/<abi>/ 目录下:
APK
└── lib/
├── armeabi-v7a/
│ └── libxxx.so
├── arm64-v8a/
│ └── libxxx.so
└── x86_64/
└── libxxx.so
加载方式 :Java 层通过 System.loadLibrary("xxx") 加载(不带 lib 前缀和 .so 后缀)。
1.8 JNI(Java Native Interface)
是什么:Java 原生接口,Java 与 C/C++ 之间的桥梁。
作用 :让 Java 代码能调用 C/C++ 编写的 .so 库中的函数,反之亦然。
流程:
Java 代码 → JNI → C/C++ 函数(.so 库中)
↑ |
└──── 返回值 ←───────────┘
1.9 Gradle externalNativeBuild
是什么:Gradle 的一个配置块,用于将 C/C++ 的原生代码构建集成到 Android 的 Gradle 构建流程中。
配置示例:
groovy
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++14"
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
作用 :告诉 Gradle 去哪找 CMakeLists.txt,以及编译哪些 ABI、用什么 C++ 标准等。
2. 基础流程
2.1 整体编译流程图
┌─────────────────────────────────────────────────────────────┐
│ 开发者编写代码 │
│ Java/Kotlin (.java/.kt) C/C++ (.c/.cpp) │
│ │ │ │
│ │ CMakeLists.txt (构建配置) │
│ │ │ │
└─────────┼──────────────────────────────┼─────────────────────┘
│ │
│ ┌─────────┘
│ │
│ Gradle externalNativeBuild
│ │
│ ▼
│ CMake 读取 CMakeLists.txt
│ (配置源文件、编译选项、目标 ABI 等)
│ │
│ ▼
│ 调用 NDK 工具链中的 Clang
│ (交叉编译:Mac/Win 上编译,产出 Android .so)
│ │
│ ┌─────┴──────┐
│ │ Clang 前端 │ 词法/语法分析 → IR
│ │ LLVM 后端 │ 优化 → 机器码
│ └─────────────┘
│ │
│ ▼
│ 生成 .so 库
│ lib/armeabi-v7a/libxxx.so
│ lib/arm64-v8a/libxxx.so
│ lib/x86_64/libxxx.so
│ │
│ ▼
│ Gradle 将 .so 打包进 APK
│ │
▼ ▼
┌──────────────────────────────────────────┐
│ APK 文件 │
│ ├── classes.dex (Java/Kotlin 编译产物) │
│ ├── lib/ │
│ │ ├── arm64-v8a/libxxx.so │
│ │ └── armeabi-v7a/libxxx.so │
│ └── res/ │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ Android 手机运行时 │
│ │
│ Java 代码 │
│ ↓ System.loadLibrary("xxx") │
│ JNI 桥梁 │
│ ↓ 调用 │
│ C/C++ 函数(.so 库中) │
└──────────────────────────────────────────┘
2.2 各工具协作关系
工具/技术 角色 作用
──────────────────────────────────────────────────────
CMakeLists.txt 配置文件 声明源文件、编译选项、输出库名等
CMake 构建系统 读取配置,调度编译过程
NDK 工具链包 提供交叉编译所需的编译器、头文件、标准库
Clang 编译器前端 把 C/C++ 源码解析成中间代码 IR
LLVM 编译器后端 优化 IR,生成目标平台机器码
Gradle 构建管理 管理整体构建流程,把 .so 打包进 APK
JNI 桥梁协议 Java 与 C/C++ 互调的接口规范
.so 产物 编译产出的共享库,打包进 APK 运行
2.3 编译执行顺序
1. Gradle 开始构建
2. Gradle 读取 externalNativeBuild 配置,找到 CMakeLists.txt
3. CMake 解析 CMakeLists.txt,生成构建脚本(ninja/Makefile)
4. 构建脚本调用 NDK 中的 Clang 编译器
5. Clang 前端:源码 → 词法分析 → 语法分析 → 语义分析 → 生成 IR
6. LLVM 后端:优化 IR → 生成目标 ABI 的机器码
7. 链接器(lld):把多个目标文件链接成 .so 库
8. Gradle 把 .so 库打包进 APK 的 lib/<abi>/ 目录
9. APK 安装到手机后,Java 通过 JNI 调用 .so 中的函数
3. 本项目相关配置
3.1 构建工具版本
| 配置项 | 值 | 配置位置 |
|---|---|---|
| AGP | 4.1.3 | build.gradle |
| NDK | 未显式指定(Gradle 自动选择) | app/build.gradle(注释了 ndkVersion '24.0.7856742 rc1') |
| CMake | 未显式指定(使用 Android Studio 内置) | CMakeLists.txt 中 cmake_minimum_required(VERSION 3.10.2) |
3.2 多设备统一建议
核心建议:在 app/build.gradle 中显式指定 ndkVersion "24.0.8215888",确保 Mac M 系列和 Windows 均兼容且编译产物一致。