简介:Android官方NDK r26b的Windows版压缩包,面向需要在Android平台使用C/C++编写高性能模块或集成既有原生库的开发者,是进行JNI开发、底层调优与交叉编译的常用工具。包内提供完整的原生开发头文件(约1969个.h文件),以及Python辅助脚本、Markdown说明文档和PDF资料,覆盖接口声明、构建配置与使用说明,可用于JNI接口调用、多架构交叉编译(如arm64-v8a、x86_64)以及静态/动态链接库生成,是配置Android Studio NDK环境后开展底层开发与调试的基础工具集。压缩包共2000个文件,整体约630.97MB,头文件按平台与API分类存放,目录结构清晰,便于按模块查阅和学习,适合作为离线NDK开发环境的重要补充。目前已有731人学习下载,适合正在学习NDK/JNI或从事游戏引擎、图像处理、音视频解码、加密算法等高性能场景开发的Android工程师作为离线参考工具包使用。
1. android-ndk-r26b-windows.zip:为什么需要离线包,它到底解决了什么
很多人在 Windows 上第一次接触 android-ndk-r26b-windows.zip,都是因为一个尴尬场景:Android Studio 里勾选了 NDK 依赖,然后卡在“正在下载”半天不动;或者公司内网机器碰不到外网,只能靠一个 zip 在手之间传来传去。这个 zip 不是某个人的备份,它是官方发布的 NDK r26b Windows 版离线分发包,里面装着一整套交叉编译工具链——clang 编译器、sysroot 系统头文件、链接器、ndk-build 脚本、CMake 工具链定义。它解决的核心问题只有一个:让你不依赖 IDE 的在线下载逻辑,也能在 Windows 上把 C/C++ 代码编译成 Android 能跑的 .so。
要读这篇文章的人,大概是三类:被 Gradle 同步折腾到想手动接管 NDK 的 Android 开发者;做跨平台原生库、需要稳定复现构建环境的技术负责人;以及第一次接触 JNI、想搞清楚“NDK 到底装了些什么”的新手。下面我从解包开始,一直讲到编译产物瘦身,全程用 r26b 这个具体版本说话。
2. 拆开 r26b 的包:目录结构、版本选型与解压前后检查
2.1 r26b 在 NDK 版本线里的定位:为什么选这个具体版本
NDK 的版本号有自己的脾气。r26 属于 26.x 分支,r26b 是这一分支里的修订维护版,修掉了一批 r26a 时期报告出来的问题,比如部分 Windows 路径处理异常和某些 API level 头文件缺失。它的工具链基于 Clang/LLVM 17 系,默认的 C++ 运行时是 libc++,平台 API 支持范围覆盖到 Android 14(API level 34)附近。对大多数做 App 内嵌原生库、游戏引擎插件或音视频处理模块的开发来说,这个版本处在“够新且经过一轮修补”的位置上。
另一个关键点:r26 本身不是 LTS 版本。如果你所在团队要求的是长期维护、极少变动的构建环境,可能会更倾向 r25b 这样的 LTS 分支;但如果你需要的是新版 API、新版 OpenSL ES 或 Vulkan 头文件、更完整的 LLVM 工具链,r26b 就是 Windows 下更合理的选择。选版本不能只看“最新”,要看你的最低 API level、Gradle 插件版本和第三方依赖对 NDK 版本的约束。r26b 在这一点上兼容性做得不错:它既能被旧版 AGP 通过 ndkVersion 指定使用,也能在没有 AGP 的纯命令行环境里独立工作。
下载时要注意,官方下载页提供的是 android-ndk-r26b-windows.zip,这是 Windows 专用的完整包,别下成 linux 或 darwin 的版本。我见过有人把三个平台的包全下了,白白多花流量,还容易在解压时弄混。如果你在 CI 机器上使用,记住下载一次之后做好本地缓存,后续构建全部指向缓存路径,避免每次都在线拉取。
2.2 下载、校验与解压路径规划
解压前先做两件事:校验文件完整性、规划安装路径。校验这一步很多人跳过,但在 Windows 上,下载工具中途断点续传导致的 zip 损坏概率不低,尤其是几 GB 的大包。官方下载页会同时给出该版本的 SHA-256 校验值,下载完成后用 PowerShell 对照一下即可。
# 在 zip 所在目录执行,把输出值和官方页面上的值比对 Get-FileHash .\android-ndk-r26b-windows.zip -Algorithm SHA256逻辑说明:Get-FileHash 是 Windows 自带的哈希计算命令,不需要额外安装工具。比对哈希能发现文件在传输过程中是否被截断或改写。如果校验值不一致,解压时轻则报错中断,重则出现某个工具链文件损坏但解压过程“看起来成功”的假象,后续编译时再随机报错。
路径规划上,我一般会避开 C 盘根目录和带空格的路径。常见误区是解压到C:\Program Files\Android\android-ndk-r26b这类位置。Program Files 中间的空格会让很多老旧的 make 脚本和 ninja 构建出现引号转义问题,虽然 r26b 自带的工具链对空格路径容忍度比老版本好,但没必要赌。建议放到D:\dev\android-ndk-r26b或C:\Android\ndk\r26b这类纯英文无空格路径。
解压时还有一个小习惯值得养成:解压出来的顶层目录应该是android-ndk-r26b,如果发现解压后目录名带了多余层级比如android-ndk-r26b\android-ndk-r26b,说明你解压时多建了一层目录,需要修正,否则后面配置环境变量会写错路径。
2.3 解开后必须认识的六个目录
解压完成后,打开目录会看到一批子目录和文件。新手容易一头雾水,但其实需要重点认识的只有六个。我列在下面,并且标注了每个目录在什么场景下会被用到。
| 目录/文件 | 作用 | 使用场景 |
|---|---|---|
toolchains/llvm/prebuilt/windows-x86_64 | 真正的编译器、链接器、llvm 工具集合 | 手动调用 clang 编译,或 CMake/Ninja 间接调用 |
platforms/android-{api} | 各 API level 的 android.jar 与头文件 | 指定-D__ANDROID_API__时被引用 |
sources/cxx-stl | libc++ 头文件与实现源码 | C++ 项目链接 STL 时使用 |
prebuilt/windows-x86_64 | 主机侧运行的工具,如 strip、addr2line | 构建产物瘦身、崩溃日志符号化 |
ndk-build.cmd | 老牌构建脚本入口 | 传统 Android.mk/Application.mk 项目 |
build/cmake | CMake 工具链文件android.toolchain.cmake | 现代 CMake 构建的核心入口 |
r26b 里platforms目录默认带多个 API level 子目录,具体到你项目里支持的最低 API 版本。在 CMake 配置时你只需给出ANDROID_PLATFORM=android-21这样的参数,工具链就会自动从platforms/android-21里取头文件。
理解这个目录结构对后面排错很有帮助。比如你在编译时报错找不到sysroot,那是因为 CMake 没拿到正确的 NDK 路径,或者工具链文件路径传错了;报错找不到liblog.so,那多半是platforms目录里的 API level 头文件不完整。我们有经验的人排查问题,第一步永远是先看路径,第二步才是看编译命令。
3. 在 Windows 上把 NDK“接”进编译环境:环境变量与两套工具链自检
3.1 ANDROID_NDK_HOME 与 ANDROID_NDK_ROOT 到底设哪个
环境变量是 Windows 上接入 NDK 的第一道坎。r26b 的文档体系里,你可能会看到三个变量:ANDROID_NDK_HOME、ANDROID_NDK_ROOT、ANDROID_NDK。它们本质上是同一个意思——告诉构建系统 NDK 的根路径在哪里——但不同工具认不同的名字。Gradle 的 AGP 主要读ndkVersion并自己查找,而 CMake 的android.toolchain.cmake在没有显式传入路径时,会尝试找ANDROID_NDK_HOME或ANDROID_NDK_ROOT。
我的习惯是两条都设,指向同一个路径,省得某些工具链“认一个不认另一个”时还要回头排查。设置方式在 Windows 11/10 上一致:打开系统属性 -> 环境变量 -> 新建用户变量。
# PowerShell 里执行也能临时验证,但系统变量要在环境变量面板里持久化 [System.Environment]::SetEnvironmentVariable("ANDROID_NDK_HOME", "D:\dev\android-ndk-r26b", "User") [System.Environment]::SetEnvironmentVariable("ANDROID_NDK_ROOT", "D:\dev\android-ndk-r26b", "User")逻辑说明:这里用SetEnvironmentVariable把两个变量都写到用户级环境变量里,User表示当前用户生效,不需要管理员权限。改完环境变量之后注意:新开的终端才会读到,已经开着的终端不会自动更新。
参数说明:ANDROID_NDK_HOME是 Android Studio 老版本和部分第三方构建脚本认的变量名;ANDROID_NDK_ROOT是 CMake 工具链和 NDK 自家脚本更偏爱的名字;两个都设才能做到兼容。如果你的项目使用 CMake 并且通过-DCMAKE_TOOLCHAIN_FILE指定了工具链文件,那么 CMake 会优先读你传入的ANDROID_NDK变量,而不是环境变量——这在后面的 4.3 节里会演示。
3.2 用 ndk-build 和 CMake 两条路分别验证工具链
环境变量设完,很多教程会告诉你“好了”,但我建议做一个可复现的自检动作,把两条工具链路径都跑一遍。先看 ndk-build 这一路。
# 假设 NDK 解压在 D:\dev\android-ndk-r26b D:\dev\android-ndk-r26b\ndk-build.cmd --version预期输出会包含 NDK 版本号,比如r26b。如果提示找不到命令或版本号异常,先检查路径是否写错,再检查环境变量是否真的生效——在 cmd 里输入echo %ANDROID_NDK_HOME%看一下有没有输出。
再看 CMake 这一路。CMake 工具链文件位置在build/cmake/android.toolchain.cmake,你可以用一条极简命令验证它能正常工作。这一步同时验证了 NDK 里的 Clang 编译器能否在你的机器上跑起来。
# 创建一个空目录,在里面用工具链文件配置一个空构建 cmake -DCMAKE_TOOLCHAIN_FILE=D:\dev\android-ndk-r26b\build\cmake\android.toolchain.cmake ^ -DANDROID_ABI=arm64-v8a ^ -DANDROID_PLATFORM=android-21 ^ -DCMAKE_BUILD_TYPE=Release ^ ..逻辑说明:-DCMAKE_TOOLCHAIN_FILE告诉 CMake 使用 NDK 提供的交叉编译配置;ANDROID_ABI指定目标 CPU 架构;ANDROID_PLATFORM指定最低 API 版本。如果命令执行后显示-- Check for working C compiler: .../clang.exe并且结果为works,说明工具链已经正确接上了。
参数说明:arm64-v8a是 64 位 ARM 架构,是目前 Android 主流设备用的 ABI。如果你要兼容老设备,后续还要加armeabi-v7a;如果要在模拟器上调试,需要x86_64。android-21这个 API level 覆盖了 Android 5.0 及以上设备,是很多库的默认底线。
3.3 验证 clang 与 sysroot:版本号里藏着兼容性
工具链能工作只是第一步,还要确认编译器版本和 sysroot 是否匹配。r26b 自带的编译器版本和 sysroot 是配套发布的,但如果你的项目里有人为指定了-DANDROID_SYSROOT指向旧版 NDK 的路径,就会出现头文件与编译器不匹配的问题。
# 直接调用 NDK 里的 clang 看版本 D:\dev\android-ndk-r26b\toolchains\llvm\prebuilt\windows-x86_64\bin\clang.exe --version输出里应该能看到 LLVM 17 系列的字样。如果版本号异常,比如显示的是 14 或 16,说明你的 PATH 环境变量里混入了其他 NDK 的 clang 路径,这会直接导致编译行为不可预期。我遇过一次 PATH 里同时存在两个 NDK 的 bin 目录,一个 r23b 一个 r26b,结果编译产物在运行时崩溃,排查了三天才发现是编译器混用导致的 ABI 不兼容。
sysroot 的检查不需要命令,你在上面 2.2 节的 CMake 验证里已经间接测过:只要 CMake 能check通过,sysroot 路径就没有问题。这里提醒一点,ANDROID_PLATFORM不要设得太低,如果你把android-21改成android-16,r26b 里并没有对应的 sysroot——r26 分支官方的最低 API 支持线就在 21 附近,设低了工具链会直接报错退场,这不是你代码的问题,是版本支持范围的边界。
4. 用 r26b 编译第一个 C 库:从 Java 声明到 so 产物的极小闭环
4.1 写一个带 JNI 导出的 native-lib.cpp
环境就绪后,我们快速跑通一个完整闭环。先写一个最简单的 C++ 文件,它导出一个 JNI 函数,Java 侧可以调用它返回一个字符串。用 NDK r26b 编译这个文件会产生一个标准的 Android .so 库。
// native-lib.cpp #include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_example_nativeapp_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from NDK r26b"; return env->NewStringUTF(hello.c_str()); }逻辑说明:JNIEXPORT和JNICALL是 JNI 规范里的导出宏,保证函数按 C 符号规则导出;函数名的Java_前缀后面跟着完整的包名加点号转下划线的路径。这里com_example_nativeapp_MainActivity对应 Java 侧com.example.nativeapp.MainActivity这个类的stringFromJNI方法。
参数说明:JNIEnv* env是 JNI 环境指针,相当于 Java 虚拟机暴露给你的接口表,NewStringUTF通过它创建 Java 层能直接使用的字符串对象。你暂时不需要深入理解它的内部机制,但要知道每个 JNI 函数调用都离不开这个指针。
4.2 用 ndk-build 编译出 armeabi-v7a 和 arm64-v8a
接着写Android.mk和Application.mk,这两个文件是 ndk-build 体系的核心。我见过不少新项目已经全面转向 CMake,但老项目的维护和第三方库的独立编译还是离不开这套。r26b 对 ndk-build 的支持依然完整。
# Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := native-lib LOCAL_SRC_FILES := native-lib.cpp LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)# Application.mk APP_ABI := armeabi-v7a arm64-v8a APP_PLATFORM := android-21 APP_STL := c++_shared逻辑说明:Android.mk里LOCAL_MODULE指定编译产物名称,最终生成libnative-lib.so;LOCAL_SRC_FILES列出要编译的源文件;LOCAL_LDLIBS链接liblog.so,这是 Android 的日志库。Application.mk里的APP_ABI决定输出哪些架构的 .so,APP_STL选择 C++ 标准库的运行方式。
参数说明:APP_STL := c++_shared表示 C++ 标准库以动态库方式随 APK 打包。如果你选c++_static,每个 .so 都会内嵌一份 libc++,包体积变大但省去了“两个 .so 共享同一份 STL”的符号冲突问题。对于库开发者,我强烈建议c++_shared,因为当你同时发布多个 .so 时,静态 STL 会导致 exceptions 跨库传递时行为诡异。
# 在 Android.mk 所在目录执行 D:\dev\android-ndk-r26b\ndk-build.cmd执行成功后,libs/目录下会出现两个 ABI 子目录,每个文件夹里各有一个libnative-lib.so。你可以用 NDK 自带的readelf工具检查产物架构是否对得上。
D:\dev\android-ndk-r26b\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-readelf.exe -h libs\arm64-v8a\libnative-lib.so | findstr Machine逻辑说明:llvm-readelf读取 ELF 文件头部,Machine字段会显示AArch64或ARM。这是验证交叉编译产物的最直接手段——看到 Machine 是AArch64而不是x86_64,说明编译过程真的在为 Android 目标工作,而不是偷偷编了个 Windows 版。
4.3 用 CMake 编译同一个库:gradle 场景下的标准做法
现如今的 Android Studio 项目默认走 CMake,所以这一节是大多数人的主战场。写下CMakeLists.txt,再用 NDK 的工具链文件去构建。
cmake_minimum_required(VERSION 3.22.1) project(native-lib) add_library(native-lib SHARED native-lib.cpp) target_link_libraries(native-lib log)cmake -S . -B build ^ -DCMAKE_TOOLCHAIN_FILE=D:\dev\android-ndk-r26b\build\cmake\android.toolchain.cmake ^ -DANDROID_ABI=arm64-v8a ^ -DANDROID_PLATFORM=android-21 ^ -DCMAKE_BUILD_TYPE=Release cmake --build build逻辑说明:cmake -S . -B build把源文件目录和构建目录分离,这是现代 CMake 的推荐姿势,避免在源码目录里产生构建垃圾。--build会调用底层构建系统,这里自动使用 Ninja 或 Makefiles,具体取决于工具链文件默认生成器——r26b 内置的 CMake 工具链默认偏好 Ninja。
参数说明:CMAKE_BUILD_TYPE=Release会加上-O3优化和-DNDEBUG宏;调试阶段建议改成Debug,那样关闭优化并保留-g调试符号,便于 adb 抓取崩溃栈。ANDROID_ABI一次只传一个,如果你想同时产出多个架构的 .so,就需要分别构建或交给 AGP 在 Gradle 层面统一处理。
构建完成后,build/目录里会出现libnative-lib.so,这是标准的内部构建产物路径。如果你把它集成进 Android Studio 的 Gradle 工程,常见做法是在build.gradle里指定 CMake 脚本路径,让 AGP 自动完成上面的全部工作,不需要手写cmake命令。
5. r26b 在 Windows 上的避坑记录:现象、原因与解决
5.1 ndk-build 提示“找不到 gcc”不是真的缺 gcc
现象:在 Windows 上执行ndk-build.cmd时,报错信息是gcc: command not found或者类似cannot find -lgcc。
原因:r26b 的工具链里只有 clang/clang++,没有 gcc。老版本 NDK 或某些网上教程里的“临时文件规则”混杂了 gcc 的编译命令引用,导致构建脚本期待一个不存在的编译器。实际问题通常出在Android.mk里的LOCAL_TOOLCHAIN_PREFIX或LOCAL_TOOLCHAIN变量被显式指定成了 gcc,或者有某个第三方 .mk 脚本硬编码了 gcc。
解决:删除Android.mk里所有指定 gcc 的变量,改用默认的 clang 工具链。r26b 自带工具链在设计上就是 Clang 第一优先,ndk-build 脚本内部封装好了,你不需要手动指定。如果错误来自第三方库,检查该库的对应版本是否支持 r26b——有些老库的构建脚本还停留在 NDK r19 时代,把-D__ANDROID_API__=21这类宏写死成旧版路径,换个维护积极的库版本是正道。你硬改第三方脚本迎合 r26b,维护成本会跟着升级。
5.2 路径带空格导致 ninja 构建失败
现象:CMake 配置阶段正常,cmake --build阶段报ninja: error: ...,错误信息里能看到路径被拆成两截,比如Program和Files各成一个 token。
原因:Ninja 对带空格的路径做转义处理,但 Windows 下如果某个 CMake 变量或编译器路径以非标准方式拼接,就会把整个路径字符串断掉。r26b 的 CMake 工具链对空格路径的容忍度有改善,但第三方自定义命令(如自定义 POST_BUILD 脚本)仍然容易翻车。
解决:最省心的方法是把 NDK 放在无空格路径下。如果你因为磁盘分区原因真的挪不动,可以试试在配置 CMake 时把编译器路径用-DCMAKE_C_COMPILER_FORCED=ON绕过编译器检测,但这治标不治本。我处理这类问题时的习惯是:先查build/CMakeCache.txt里是否有带空格路径的变量,如果是,直接用cmake -U清掉对应变量重新配置。与其跟空格路径搏斗,不如重装到D:\dev\ndk。
5.3 模拟器上跑不起来的真凶:x86_64 ABI 没编进去
现象:在 Windows 上用 Android 模拟器调试,App 一启动就报java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader ... couldn't find "libnative-lib.so",但真机上运行正常。
原因:r26b 编译时APP_ABI里只写了armeabi-v7a和arm64-v8a,而 Windows 上的 Android 模拟器绝大多数运行在 x86_64 指令集下。模拟器无法运行 ARM 架构的 .so,系统也找不到一个能匹配的库文件,于是直接崩。
解决:临时调试时把x86_64加进Application.mk的APP_ABI,或者在你的 Gradle 构建的abiFilters里加上"x86_64"。发布时记得移除,否则包体积白白大一圈。另外一个血泪经验是,如果你的 App 里有多个 .so 且只有部分模块包含 x86_64 版本,系统会因为找不到某个依赖库而报另一个库“找不到”——先检查所有 .so 的 ABI 是否齐全,再查具体缺失项。模拟器上的 ABI 问题基本每次都是这个规律。
5.4 Gradle ndkVersion 与本地 r26b 不一致触发自动下载
现象:项目里的build.gradle写了ndkVersion "26.1.4718422",而本地解压的是android-ndk-r26b。构建时 Gradle 提示“NDK did not have a source.properties file”或者干脆自动触发下载,下载失败后整个构建挂掉。
原因:AGP 对 NDK 的版本识别依赖 NDK 目录里的source.properties文件,其中的Pkg.Revision字段会写版本号。r26b 的版本字符串是26.1.4718422,如果你的本地目录名是android-ndk-r26b但source.properties内容异常或缺失,AGP 就不认账。
解决:先检查 NDK 目录下的source.properties,确认Pkg.Revision = 26.1.4718422。如果没有这个文件,说明解压不完整或者下载包本身有问题——重新解压官方 zip 即可。如果文件存在但 Gradle 还是要下载,在local.properties里显式指定本地 NDK 路径:
sdk.dir=D:\\Android\\Sdk ndk.dir=D:\\dev\\android-ndk-r26b逻辑说明:local.properties是 Gradle 读取本地环境配置的文件,优先级高于系统环境变量。ndk.dir指向本地解压好的 NDK 根目录,AGP 读到这里就不会再去网络下载。这里要留意反斜杠的转义,在 properties 文件里写成双反斜杠,否则路径解析会出错。
5.5 旧项目里 jni.h 报错要往 API level 上查
现象:接手一个老项目,代码里没有动过任何 JNI 相关文件,升级到 r26b 后jni.h或者nativehelper/jni.h开始报错,错误行指向某个宏定义处。
原因:老项目的Android.mk里经常用APP_PLATFORM := android-19或更低版本,而 r26b 的 sysroot 最低只支持到 android-21。API level 19 的系统头文件在 r26b 里根本不存在,编译器找到的可能是被 include 路径错误引导的宿主机头文件,两者混在一起就会爆出莫名其妙的宏错误。
解决:把APP_PLATFORM提升到android-21或更高。如果你的 App 声称支持 Android 4.x,那需要换回 r25b 这类支持老 API 的 NDK 版本,而不是硬用 r26b——工具链不是越新越能向下兼容,它有明确的版本边界。另外,部分老项目里手动写了-I的头文件搜索路径指向旧版 NDK,这类硬编码路径在升级时要一并清掉。
6. 进阶:把 r26b 用出生产力——ABI 裁剪、strip 与多个构建入口并存
到这里,基本编译和环境配置已经不是障碍。最后这一章我把日常做原生库时真正拉开效率的三个操作讲透,它们能让你的构建脚本看起来更专业,也让 CI 产物更干净、更快。
第一个是 ABI 矩阵的收敛。很多项目的Application.mk里APP_ABI写的是all,这会让 r26b 同时产出 arm64-v8a、armeabi-v7a、x86、x86_64 四份 .so。实际线上设备几乎只剩 arm64-v8a,真机上 32 位 ABIs 正在被系统逐步淘汰。我在团队里推动的做法是:Debug 构建保留x86_64以便模拟器调试,Release 构建只留arm64-v8a。用 Gradle 表达就是下面这样:
android { defaultConfig { externalNativeBuild { cmake { abiFilters += "arm64-v8a" } } } }这段配置写在build.gradle里,abiFilters会约束 AGP 只构建指定 ABI。如果你的 Release 由命令行驱动,也可以在执行 cmake 时只传-DANDROID_ABI=arm64-v8a,两种方式效果相同。这类裁剪最直接的收益是 APK 体积显著下降,一个未压缩的 .so 常常十几 MB,只留一套能小掉一半以上。
第二个是 strip 的精确定位。r26b 编译 Release 版 .so 时默认已经做了剥离,但如果你用CMAKE_BUILD_TYPE=RelWithDebInfo,会保留符号表用于崩溃分析。这种情况下发布前记得手动 strip 一次,剥离调试符号后的 .so 体积会缩水 20% 到 40%。工具位置是:
D:\dev\android-ndk-r26b\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-strip.exe -s libs\arm64-v8a\libnative-lib.so-s参数表示只移除符号表,不对代码段做任何指令级操作,安全且不可逆。我习惯在 CI 脚本里把 strip 放在打包 APK 之前一步执行,这样线上崩溃时虽然看不到符号,但崩溃地址配合addr2line和构建时保存的libnative-lib.so.dbg副本,依然能还原出函数名。保留一份未 strip 的副本是好习惯,否则日后线上崩溃会陷入无源码可比对的僵局。
第三个操作是让多个 NDK 版本在同一台机器上共存并随时切换。r26b 解压后目录独立,只要环境变量指向当前项目需要的那个,就是天然的多版本共存。我本地常驻两个版本,目录命名带上版本号后缀,比如D:\dev\ndk\r25b和D:\dev\ndk\r26b。切换时不需要卸载任何一个,只需要在local.properties里改一行ndk.dir。这个习惯救过我一次——某个老项目因为 STL 实现差异必须在 r25b 上编译,当时我正因为强迫症留下了这个老包,半小时内完成了切换,同事则花了半天重新下载。
最后说一句不吐不快的教训:NDK 版本是构建链路上最容易被忽略却又最影响结果的因素。我在曾经的项目里吃过工具链混用的黑匣子亏,之后每次集成新的 NDK 都在第一次配置时干了同一个动作——把source.properties复制到项目根目录留档。一旦后续构建有问题,这份文件就是整个工具链的身份证。希望这篇从解压到进阶的记录能帮你少走一段我刚才走过的弯路,也希望你在 Windows 上用 r26b 编译一路顺到底。
本文还有配套的精品资源,点击获取