简介:面向Android开发者的SDK Build-Tools 29.0.2离线安装包,对应API 29(Android 10)构建工具链,解决缺少对应版本build-tools导致Gradle构建失败或SDK组件不全的问题,适合需要使用targetSdk 29或维护旧项目的开发者。压缩包共173个文件,大小约37.16MB,涵盖AAPT2、aapt、dx/d8、R8、zipalign、apksigner等可执行程序,以及对应的头文件、动态库、RenderScript相关bc/rsh文件、jar库和bat调用脚本,可支撑资源编译、dex转换、代码混淆、APK对齐与签名等完整构建流程。已有1379人学习/下载,是快速补齐Android构建环境的实用资源。借助这个压缩包不仅可离线安装到android-sdk/build-tools目录,还能了解构建工具链的组成与作用,便于排查构建报错并加深对Android打包机制的理解。
1. 为什么 29.0.2 这个构建工具包常被单独下载
做 Android 开发的老手几乎都见过这个场景:项目的compileSdkVersion停在 29,buildToolsVersion写死"29.0.2",但新装的 Android Studio 默认只带最新的 build-tools,一到gradle assembleDebug就报Failed to find Build Tools revision 29.0.2。去 SDK Manager 里勾选下载,要么网络不给力,要么公司内网根本连不上 Google 的仓库。这时候手头要是有一份build-tools.29.0.2.zip,几分钟就能解决问题。这篇笔记我结合自己拆过的离线构建环境,把这份资源的目录结构、部署方式、Gradle 对接和踩过的坑一次说清,新手能照步骤走,熟手可以直接跳到第 4 章看排查记录。
2. 29.0.2 到底装了什么:工具清单与版本定位
build-tools 本质上是一套独立于 Android Studio 的底层编译与打包工具集。29.0.2 是 Android 10 时代的稳定版本,却在 2024 年仍被大量存量项目引用,不是因为新,而是因为稳。很多团队的 CI 流水线还锁在旧版 AGP,升级 build-tools 会导致 AAPT2 资源编译结果变化,甚至影响多模块增量编译的缓存命中,所以没人敢轻易动。
2.1 压缩包里都有哪些可执行文件
直接解压build-tools.29.0.2.zip,里面是一个与版本号同名的根目录,里面放着一批命令行可执行文件。我按用途分成四类:
| 工具名 | 用途 | 典型命令 |
|---|---|---|
aapt2 | 资源编译与链接,生成R.java和编译后的资源 | aapt2 compile --dir res -o res.zip |
aapt | 旧版资源打包工具,连接资源表与 AndroidManifest | aapt dump badging app.apk |
zipalign | 对齐 APK 内部的未压缩数据,优化运行期访问 | zipalign -v 4 in.apk out.apk |
apksigner | APK v1/v2/v3 签名工具(替代旧的jarsigner) | apksigner sign --ks key.jks app.apk |
d8 | Java 字节码转 Dalvik 字节码(替代dx) | d8 --release --lib android.jar input.jar |
dx | 老式 dex 化工具,部分旧 AGP 仍会调用 | dx --dex --output=classes.dex input.jar |
split-select | 按屏幕密度/ABI 分割资源包 | split-select --target-density xxhdpi |
renderscript | RenderScript 编译前置工具 | renderscript -o res/raw output.rs |
jobb | 构建 OBB 扩展文件 | jobb -d input_dir -o game.obb -pn com.example |
不要小看这份清单。很多开发者以为 build-tools 里只有aapt2,实际上apksigner和zipalign在手工校验 APK 签名时比 Android Studio 里点按钮更可控。d8在老项目迁移到 AndroidX 时也经常被单独拎出来用,比如离线把某份第三方 jar 转成 dex。
2.2 为什么推荐 29.0.2 而不是最新版
从我接触的模拟项目 X 来看,至少三类场景必须锁定 29.0.2:
第一,AGP 版本与 build-tools 存在软绑定。AGP 3.x 到 4.x 时代,build-tools默认版本跟着 AGP 走,但项目里显式声明buildToolsVersion = "29.0.2"时,AGP 会老老实实用这个版本。如果你把 build-tools 换成 34.0.0,资源编译器的输出格式兼容性通常没问题,但某些老式第三方插件直接调用build-tools/29.0.2/aapt的硬编码路径,找不到文件构建立刻失败。
第二,团队统一构建环境。同一个项目,有人本机 build-tools 34,有人 33,打出的 APK 在包体结构和资源 ID 分配上存在细微差异,上线后 bugly 上报的堆栈行号对不齐,排查崩溃时极痛苦。统一锁 29.0.2 能保证 CI 与本地产物一致。
第三,APK 体积与兼容性取舍。新 build-tools 默认对资源做更激进的压缩和去重,某些 SDK 里的同构资源会被误判,导致运行时Resources$NotFoundException。29.0.2 年代这种行为还没有普遍化,对老 SDK 兼容性更好。
顺便说一句,29.0.2 是 29.x 系列里被 fork 得最多的一版。当时 AAPT2 修复了一个资源重写时的编译崩溃,且没有引入 29.0.3 对某类混淆规则的新警告,许多大厂模板项目都锁这个版本。这不是玄学,是社区踩过坑后选出的平衡点。
3. 手动部署到 Android SDK 目录:三步走与参数说明
拿到 zip 之后最直接的做法是通过 SDK Manager 安装,但离线环境下必须手动放。核心原则是:解压后目录名必须与版本号完全一致,并且放在 SDK 根目录下的build-tools文件夹里,Gradle 才能从标准位置找到它。
3.1 目录约定与校验
先确认本机 Android SDK 的位置。macOS 默认在~/Library/Android/sdk,Linux 常在~/Android/Sdk或/opt/android-sdk,Windows 常见于%LOCALAPPDATA%\Android\Sdk。我一般会先打印环境变量:
echo $ANDROID_HOME ls -d $ANDROID_HOME/build-tools 2>/dev/null || echo "build-tools 目录不存在"正常情况下build-tools目录下会存在34.0.0、33.0.1这类子文件夹。如果没有build-tools目录,说明 SDK 装得不完整,先手动创建。这一步能提前发现路径配置问题,避免后面 Gradle 报错时怀疑人生。
注意ANDROID_HOME和ANDROID_SDK_ROOT两个环境变量在 Windows 平台可能同时存在,Gradle 会优先读ANDROID_SDK_ROOT。如果两者指向不同路径,手动装的 build-tools 可能部署到了错误目录,这是最常见的"装好却找不到"的原因。
3.2 解压与放置
拿到build-tools.29.0.2.zip,我习惯先在临时目录解压,确认内部结构后再拷贝,防止 zip 解压出的根目录名不是 29.0.2。
unzip build-tools.29.0.2.zip -d /tmp/bt ls -l /tmp/bt # 看到 /tmp/bt/29.0.2 或者 /tmp/bt/build-tools/29.0.2 两种可能如果是第二种,说明 zip 里已经带了一层build-tools父目录,直接整体拷贝;如果是第一种,则要手动创建目标目录再移动:
mkdir -p $ANDROID_HOME/build-tools cp -r /tmp/bt/29.0.2 $ANDROID_HOME/build-tools/ chmod +x $ANDROID_HOME/build-tools/29.0.2/*最后一步chmod在 Linux/macOS 上必须做。Windows 环境下解压自带可执行权限,但 Linux 服务器上常见翻车点就是少了执行权限,运行aapt2时报Permission denied。拷贝完成后验证一下版本号输出。
3.3 让 Gradle 认到这个版本
部署完成后的静态检查:
$ANDROID_HOME/build-tools/29.0.2/aapt2 version # 输出类似 Android Asset Packaging Tool (aapt2) 29.0.2-...项目侧的配置才是关键。打开app/build.gradle,确认三个字段没有冲突:
android { compileSdkVersion 29 buildToolsVersion "29.0.2" defaultConfig { minSdkVersion 21 targetSdkVersion 29 } }这里有个容易忽略的细节:buildToolsVersion必须写成字符串字面量,不能写成29.0.2数字,否则 Gradle Groovy 会解析为29.02并尝试查找29.02版本目录,直接报错。我在某跨平台系统的打包脚本里就遇到过这种低级错误,排查了半天还以为是 zip 没放对位置。
设置好后执行一次./gradlew app:assembleDebug,观察日志里是否出现Build Tools revision 29.0.2。如果 Gradle 仍提示找不到,优先检查项目是否在android块之外又重新指定了buildToolsVersion或者父级build.gradle里覆盖了 SDK 路径。
3.4 命令行工具验证
构建成功后,还可以用aapt2 dump检查 APK 里的资源版本信息:
$ANDROID_HOME/build-tools/29.0.2/aapt2 dump badging app/build/outputs/apk/debug/app-debug.apk | head -n 5正常能看到package: name=... versionCode=...。如果此处报错,说明 APK 资源表损坏或aapt2与 APK 的 SDK 版本不匹配,需要重新构建。这一步同时验证了 build-tools 与项目产物之间的兼容性。
4. 常见问题排查:我在这份资源上翻过的四个坑
离线部署 build-tools 本身不难,难的是部署完之后的连锁反应。整理几条真实踩坑记录,每条都按"现象 → 原因 → 解决"的结构写,你在自己环境大概率会遇到其中至少一条。
4.1 现象:Gradle 始终提示找不到 29.0.2,但文件明明存在
原因:SDK 目录被 Gradle 重新定位。项目根目录
local.properties里的sdk.dir指向了另一套 SDK,或者ANDROID_HOME与ANDROID_SDK_ROOT指向不一致。我遇到的情况是 Linux 服务器上ANDROID_HOME指向/opt/android而local.properties指向/home/user/Android/Sdk,build-tools 装到了前者,构建读的是后者。解决方法是统一环境变量,并删除项目里的local.properties改由 CI 注入,或者在local.properties中显式写死sdk.dir=/opt/android。
4.2 现象:aapt2报libc++.so: cannot open shared object file
原因:29.0.2 时代的
aapt2依赖系统动态库,部分精简版 Linux 镜像缺少libstdc++6或libc++。这不是 zip 本身的问题,而是运行环境缺依赖。解决办法是安装对应动态库。Ubuntu 上执行apt-get install -y libstdc++6,CentOS 上yum install -y libstdc++.x86_64。如果你在 Docker 容器里,记得在基础镜像里预先加入这些库,而不是等构建到一半再装。
4.3 现象:手动部署后 Android Studio 的 SDK Manager 里显示 "Not installed"
原因:SDK Manager 的安装状态记录文件(位于
$ANDROID_HOME/.knownPackages或package.xml)没有同步更新,Studio 认为目录是外部导入的。解决方式有两种。一种是忽略提示,只要 Gradle 构建能过就不管;另一种是在 SDK Manager 里点一次"取消并重试"刷新,或者删除 SDK 根目录下的.knownPackages和temp相关状态文件后重启 Studio。我一般选前者,因为 Studio 的提示不影响命令行构建。
4.4 现象:升级 AGP 后,apksigner无法验证 V2 签名
原因:29.0.2 的
apksigner默认启用 V2,但 AGP 升级到 7.x 后,部分构建脚本仍调用旧版apksigner做二次签名,新签名块覆盖了旧签名。解决方法是统一签名工具链:要么在android块里显式指定signingConfig交给 AGP 处理,要么在构建脚本里把apksigner路径换成$ANDROID_HOME/build-tools/34.0.0/apksigner重签。这里有个判断技巧:用 29.0.2 的apksigner verify --v2检查刚打出的包,如果返回DOES NOT VERIFY,基本就是二次签名导致。
4.5 现象:d8提示--output目录不存在
原因:
d8不会帮你创建输出目录,常见于程序化调用时把输出指到不存在的build/dex路径。解决:先mkdir -p build/dex再执行d8。这个坑很反直觉,因为很多人以为编译工具会自动建目录,但d8的定位是核心工具,不做任何隐式行为。脚本里加上mkdir -p一行后,这个问题再也没出现过。
5. 实战:一份能用 29.0.2 完整构建的脚本
前面解决了部署和定位,这一章直接给一套可复制的构建流程。场景是模拟项目 X,一个公司内部 App,需要完全离线构建并且输出对齐签名后的 APK。整个过程用 Gradle 加命令行组合完成,不依赖 Android Studio。
5.1 配置与依赖
项目根目录只需要三样东西:Android SDK(含 29.0.2)、JDK 8 或 11、Gradle 发行包。我偏好使用 Gradle Wrapper 固定版本,避免本机环境差异。在gradle/wrapper/gradle-wrapper.properties里指定:
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip如果用老版本 AGP,gradle-6.1.1 是稳妥选择。注意 Gradle 与 AGP 之间存在兼容矩阵,AGP 3.5.3 配 Gradle 6.1.1 属于常见组合,不要盲目追新。
5.2 构建命令
export ANDROID_HOME=/opt/android-sdk export ANDROID_SDK_ROOT=$ANDROID_HOME cd /workspace/projectX ./gradlew --offline assembleRelease -Pandroid.injected.build.abi=x86_64--offline告诉 Gradle 不访问远程仓库,所有依赖必须提前放入本地缓存。-Pandroid.injected.build.abi是 AGP 支持的一个参数,强制只为指定 ABI 生成 dex,能显著缩短构建时间。如果你的 App 需要多 ABI,去掉这个参数即可。日志里重点观察BUILD SUCCESSFUL前的Task : app:packageRelease是否执行,这是打包核心任务。
5.3 构建后处理:zipalign 与签名
打包出来的原始 APK 未对齐、未签名,需要两步处理:
BT=$ANDROID_HOME/build-tools/29.0.2 $BT/zipalign -f -v 4 app/build/outputs/apk/release/app-release-unsigned.apk app/build/outputs/apk/release/aligned.apk $BT/apksigner sign --ks keystore.jks --ks-key-alias myalias --ks-pass pass:123456 --out app-release-signed.apk aligned.apkzipalign的-v 4是指 4 字节对齐,APK 内部的.so库和资源文件需要这个对齐以满足mmap访问条件。-f表示强制覆盖已存在的输出文件。apksigner的--ks-pass pass:123456是明文密码,生产环境建议改成交互式env:PASS_VAR注入变量避免密码泄露。
两个命令的执行顺序不能反。先zipalign再签名是官方推荐做法,因为签名后对齐会破坏签名块。30.0.0 之后的 build-tools 可以先用apksigner签名再zipalign -p,但 29.0.2 只支持先对齐后签名。
5.4 失败时看哪里
离线构建中翻车,先看三个地方。第一,app/build/outputs/logs/manifest-merger-*.txt,如果 manifest 合并失败,大概率是依赖版本冲突,这里会打出具体冲突三方。第二,app/build/intermediates/incremental目录下的.bb文件,增量编译失败时这些文件会记录错误坐标,通读一遍能定位资源处理任务。第三,Gradle 的--info日志里搜Executing task和Skipping task,确认每个 Task 是否真的执行,避免被缓存误导。我见过有人改了 build-tools 版本但 Task 仍走缓存,导致一直用旧版工具,必须清理app/build和~/.gradle/caches里的对应缓存才能生效。
6. 进阶技巧:用 29.0.2 验证 APK 的合法性
构建产物交付前,我通常会做三层验证:签名有效性、资源完整性、对齐状态。这套流程用自己的工具比 Android Studio 的图形界面更快,而且方便写进 CI。
第一层,校验签名与证书有效期:
$ANDROID_HOME/build-tools/29.0.2/apksigner verify --print-certs app-release-signed.apk输出中的Signer #1 certificate DN与证书指纹需要与目标一致,apt上证书的 SHA-256 离线和线上相同才是合法发布。这里最容易忽略的是 V1 与 V2 签名的差异。29.0.2 的apksigner默认只验证 V2 签名,如果 APK 只有 V1 签名,会直接报DOES NOT VERIFY。加上--v1-signing-enabled参数或用verify --verbose查看当前激活的签名方案:
$ANDROID_HOME/build-tools/29.0.2/apksigner verify --verbose app-release-signed.apk第二层,检查资源表是否存在漂移:
$ANDROID_HOME/build-tools/29.0.2/aapt2 dump resources app-release-signed.apk | grep -E "^resource" | head -n 20如果发现资源 ID 与上一个版本的 APK 出现整体偏移,说明构建环境存在不可重复问题,应检查 build-tools 版本是否被某个 Task 意外替换。这层检查我每次发版前必做,能拦截掉很多"小版本更新但资源大搬家"的隐患。
第三层,验证对齐与压缩比:
$ANDROID_HOME/build-tools/29.0.2/zipalign -c -v 4 app-release-signed.apk返回Verification succesful说明 4 字节对齐符合要求。负压测试,打出未对齐的包再检查,你会看到大量Bad alignment输出。
最后,我习惯把这三条命令包进一个verify.sh,在发布脚本里和 APK 一起执行。从那以后,每次发版前我都强制走一遍 apksigner、aapt2、zipalign 三连,十次里有九次能在构建产物里发现问题,剩下一次是版本号忘改。这套习惯帮我省掉了很多用户在应用商店下载后的崩溃反馈,希望也能帮到你。
本文还有配套的精品资源,点击获取