☰
Android SDK Build-Tools 29.0.2 离线安装与编译打包全指南
2026/10/15 17:59:38 网站建设 项目流程

简介:Android SDK Build-Tools 29.0.2 是面向 Android 应用开发者、构建工程师及移动端学习者的核心构建工具集,对应 Android 10(API 29)版本,用于完成资源编译、字节码转换、代码混淆、APK 对齐与签名等构建环节。压缩包共 173 个文件,约 37.16MB,以 85 个 h 头文件、24 个 so 动态库、17 个 rsh 脚本、13 个 exe 可执行程序、7 个 jar 包及 6 个 dll 为主,另含 bat 批处理、bc 与 a 静态库等,覆盖 AAPT2、d8/dx、R8、Zipalign、apksigner 等工具的运行依赖。已有 1372 人学习下载,适合需要配置本地 SDK 环境、排查构建报错或研究打包流程的开发者参考。通过该工具集可理解资源编译、dex 生成、代码缩减与 APK 签名等关键机制,为 Gradle 自动化构建与版本兼容性调试提供完整支撑。

1. 构建工具包 29.0.2 到底解决了什么问题

如果你在 CI 日志里见过Failed to install the following Android SDK packages as some licences have not been accepted或者Could not find build-tools;29.0.2,那你大概率已经和这个压缩包打过照面了。build-tools.29.0.2.zip是 Android SDK 构建工具链中一个特定版本的离线分发包,里面装着 aapt2、d8、zipalign、apksigner 这些编译打包环节的核心可执行文件。它不负责写代码,也不负责跑模拟器,它只干一件事:把你写好的资源和字节码,变成能装进手机的 APK 或 AAB。

这个标题背后的人群很明确——需要在内网环境、离线机器或者 CI 缓存节点上复现 Android 构建流程的工程师。你可能遇到的情况是:公司构建机不允许直连外部仓库,或者 SDK Manager 拉取超时反复失败,又或者某个老项目锁死了 build-tools 版本,升级就崩。这时候一个完整的 29.0.2 离线包就是救命稻草。接下来我会按“它是什么、怎么装、怎么配、坑在哪”的顺序,把这条链路拆开讲清楚。

2. 拆开 build-tools 29.0.2:目录结构与工具链分工

2.1 压缩包里到底有什么

拿到build-tools.29.0.2.zip之后,不要急着解压到任意目录。先看一眼它的内部结构,这决定了你后面怎么配环境变量。标准的分发包解压后是一个以版本号命名的文件夹,里面通常包含以下核心文件:

文件名作用是否可缺失
aapt2资源编译与链接,生成 flat 文件不可缺失
aapt旧版资源打包工具,部分老项目仍在用建议保留
d8Java 字节码转 Dalvik 字节码不可缺失
dx旧版 dex 工具,已被 d8 取代可缺失
zipalign对齐 APK 内未压缩数据不可缺失
apksignerAPK 签名与签名校验不可缺失
lib64/动态链接库目录,d8 和 aapt2 依赖不可缺失
source.properties版本元数据,AGP 靠它识别版本不可缺失

其中source.properties最容易被忽略。它里面写着Pkg.Revision=29.0.2,Android Gradle Plugin 在配置阶段会读取这个文件来确认 build-tools 版本是否匹配。如果你手动拼凑文件却漏了这个,Gradle 会直接报Failed to find Build Tools revision 29.0.2,而不是告诉你缺了哪个文件。

2.2 为什么是 29.0.2 而不是 30 或 34

build-tools 的版本号和 Android API 级别并不是一一对应的,但 29.0.2 这个版本有它的特殊性。它对应的是 Android 10(API 29)时代的工具链,aapt2 在这个版本已经相当稳定,d8 对 Java 8 字节码的支持也成熟了。很多存量项目——尤其是那些 targetSdk 还停留在 28 或 29 的——在 AGP 3.5 到 4.1 之间锁死了这个版本。

升级到 30 以上会引入什么?最典型的是 aapt2 对资源命名空间的校验更严格,一些老项目里不规范的自定义属性会直接编译失败。另一个是 d8 在 30 之后默认开启了一些脱糖策略,如果你的依赖里有老版本库,可能会遇到Invoke-customs are only supported starting with Android O这类运行时崩溃。所以不是新版本不好,而是老项目的构建链路经不起折腾。29.0.2 是一个被大量验证过的稳定锚点。

2.3 离线安装的标准操作路径

假设你拿到的是一个干净的 Linux 构建机,没有 Android SDK,也没有网络。第一步是确定 SDK 根目录。常见做法是在/opt/android-sdk或者用户目录下建一个android-sdk文件夹,然后把 build-tools 放进去。

# 创建 SDK 根目录结构 mkdir -p /opt/android-sdk/build-tools # 解压离线包到指定版本目录 # 注意:解压后目录名必须是 29.0.2,不能带其他后缀 unzip build-tools.29.0.2.zip -d /opt/android-sdk/build-tools/ # 验证目录结构是否正确 ls /opt/android-sdk/build-tools/29.0.2/ # 应该看到 aapt2、d8、zipalign、apksigner、source.properties 等

解压之后要确认文件权限。从某些渠道拿到的 zip 包在打包时可能丢失了可执行位,导致aapt2无法运行。用chmod +x给所有可执行文件补上权限:

# 批量补可执行权限 chmod +x /opt/android-sdk/build-tools/29.0.2/aapt2 chmod +x /opt/android-sdk/build-tools/29.0.2/aapt chmod +x /opt/android-sdk/build-tools/29.0.2/d8 chmod +x /opt/android-sdk/build-tools/29.0.2/zipalign chmod +x /opt/android-sdk/build-tools/29.0.2/apksigner # 验证 aapt2 能否正常运行 /opt/android-sdk/build-tools/29.0.2/aapt2 version # 预期输出:Android Asset Packaging Tool (aapt) 2.19-...

这里有个细节:aapt2 version的输出里会带一个构建号,不同渠道拿到的包可能略有差异,但只要主版本是 2.19 就说明是 29.0.2 对应的工具链。如果输出command not found或者Permission denied,先查权限,再查动态链接库是否完整。

2.4 在 Gradle 里指定本地 build-tools 路径

光把文件放好还不够,Gradle 需要知道去哪里找。有两种方式:一种是在local.properties里写sdk.dir,另一种是在build.gradle里显式指定buildToolsVersion。

// 在 app 模块的 build.gradle 中 android { compileSdkVersion 29 buildToolsVersion "29.0.2" // 必须和实际目录名完全一致 defaultConfig { targetSdkVersion 29 // ... 其他配置 } }

同时在项目根目录的local.properties里写:

# local.properties 不要提交到版本库 sdk.dir=/opt/android-sdk

这样 AGP 会去/opt/android-sdk/build-tools/29.0.2/找工具。如果路径不对,报错信息通常是Failed to find Build Tools revision 29.0.2,这时候优先检查目录名是否有多余空格或大小写问题。Linux 下大小写敏感,29.0.2和29.0.2是两个不同的目录。

3. 用命令行手动跑通一次编译打包链路

3.1 从资源到 APK 的最小命令序列

很多人用 Android Studio 点一下 Run 就完事了,但离线环境下你需要知道底层到底调了什么。下面这条链路是 build-tools 29.0.2 里最核心的四个步骤,每一步都可以单独执行和验证。

第一步是用 aapt2 编译资源文件。假设你有一个res目录,里面放着布局和图片:

# 编译单个资源文件为 flat 格式 /opt/android-sdk/build-tools/29.0.2/aapt2 compile \ --dir res/ \ -o compiled_res.zip # 参数说明: # --dir res/ 指定资源目录 # -o 输出编译后的 zip 包 # 注意:aapt2 compile 只做编译,不做链接

第二步是链接资源并生成 R.java 和资源 APK:

# 链接编译后的资源 /opt/android-sdk/build-tools/29.0.2/aapt2 link \ -I /opt/android-sdk/platforms/android-29/android.jar \ --manifest AndroidManifest.xml \ -o app_res.apk \ compiled_res.zip # 参数说明: # -I 指定 android.jar 路径,提供系统资源引用 # --manifest 指定清单文件 # -o 输出链接后的资源 APK # 这一步会生成 R.java,但不会编译 Java 代码

第三步是用 d8 把 Java 字节码转成 dex:

# 将多个 class 文件打包成 dex /opt/android-sdk/build-tools/29.0.2/d8 \ --output ./dex_output/ \ --lib /opt/android-sdk/platforms/android-29/android.jar \ ./classes/com/example/*.class # 参数说明: # --output 指定 dex 输出目录 # --lib 指定 android.jar 作为库依赖 # 最后是输入的 class 文件路径 # d8 会自动处理脱糖和最小化

第四步是用 zipalign 对齐并 apksigner 签名:

# 先对齐 /opt/android-sdk/build-tools/29.0.2/zipalign -v 4 app_unsigned.apk app_aligned.apk # 再签名 /opt/android-sdk/build-tools/29.0.2/apksigner sign \ --ks my-release-key.jks \ --ks-key-alias mykey \ --out app_signed.apk \ app_aligned.apk # 参数说明: # zipalign -v 4 4 表示 4 字节对齐,这是 Android 要求的 # apksigner sign 需要 keystore 和别名 # --out 指定签名后的输出文件

3.2 每一步失败时看什么日志

aapt2 compile 失败最常见的原因是资源文件里有非法字符或者 XML 格式错误。报错会直接指出文件名和行号,比如error: invalid character in resource name。这时候不要怀疑工具链,去改资源文件。

aapt2 link 失败通常是android.jar路径不对,或者 manifest 里引用了不存在的资源。报错resource not found后面会跟具体的资源 ID,用grep在 res 目录里搜一下就能定位。

d8 失败最常见的是Type com.example.XXX is defined multiple times,说明 classpath 里有重复的类。检查你的依赖列表,排除重复引入的库。另一个高频错误是Cannot fit requested classes in a single dex file,这是方法数超限,需要开启 multidex。

zipalign 失败一般是输入 APK 本身有问题,比如上一步 d8 没有正确生成 dex 文件。先确认 APK 里确实有classes.dex,再用unzip -l看一眼结构。

apksigner 失败最常见的是 keystore 密码错误或者别名不对。报错Failed to load keystore或者No key with alias。用keytool -list -keystore my-release-key.jks确认别名拼写。

3.3 用脚本把四步串起来

手动敲四步太累,写一个 shell 脚本把参数抽出来:

#!/bin/bash # build_apk.sh - 离线构建脚本示例 set -e # 任何一步失败就退出 BT=/opt/android-sdk/build-tools/29.0.2 PLATFORM=/opt/android-sdk/platforms/android-29/android.jar RES_DIR=./res MANIFEST=./AndroidManifest.xml CLASSES_DIR=./classes OUT_DIR=./build_output mkdir -p $OUT_DIR echo "Step 1: Compiling resources..." $BT/aapt2 compile --dir $RES_DIR -o $OUT_DIR/compiled_res.zip echo "Step 2: Linking resources..." $BT/aapt2 link -I $PLATFORM --manifest $MANIFEST \ -o $OUT_DIR/app_res.apk $OUT_DIR/compiled_res.zip echo "Step 3: Dexing classes..." $BT/d8 --output $OUT_DIR/dex_output/ --lib $PLATFORM \ $CLASSES_DIR/com/example/*.class echo "Step 4: Aligning and signing..." # 这里需要先把 dex 和资源合并成完整 APK,省略合并步骤 $BT/zipalign -v 4 $OUT_DIR/app_unsigned.apk $OUT_DIR/app_aligned.apk $BT/apksigner sign --ks my-release-key.jks --ks-key-alias mykey \ --out $OUT_DIR/app_signed.apk $OUT_DIR/app_aligned.apk echo "Build complete: $OUT_DIR/app_signed.apk"

这个脚本里set -e是关键,任何一步返回非零就停住,避免错误累积。实际项目中资源编译和 dex 生成是并行的,但离线调试时串行更容易定位问题。脚本里的路径都是硬编码,迁移到其他机器时记得改BT和PLATFORM两个变量。

4. 避坑指南:build-tools 29.0.2 离线部署的五个翻车现场

4.1 解压后目录名带后缀导致 AGP 识别失败

现象:Gradle 报Failed to find Build Tools revision 29.0.2,但你明明已经把文件放到了build-tools/下面。

原因:从某些渠道下载的 zip 包解压后目录名可能是build-tools.29.0.2或者29.0.2-preview,而 AGP 只认纯版本号29.0.2。

解决:解压后手动重命名目录,确保ls /opt/android-sdk/build-tools/的输出里只有29.0.2这一个文件夹。如果同时存在多个版本,AGP 会按buildToolsVersion精确匹配,不会自动选最新的。

4.2 source.properties 缺失或内容不完整

现象:目录名对了,但 Gradle 仍然报找不到 build-tools,或者报Build Tools revision 29.0.2 is corrupted。

原因:source.properties文件丢失,或者里面的Pkg.Revision值被改过。有些手动打包的离线包会漏掉这个文件。

解决:手动创建source.properties,内容如下:

Pkg.Revision=29.0.2 Pkg.Desc=Android SDK Build-Tools 29.0.2

保存后重新跑 Gradle,如果还报错,用cat确认文件编码是 UTF-8 无 BOM,Windows 下编辑过的文件可能带 BOM 头导致解析失败。

4.3 aapt2 在 Alpine Linux 上跑不起来

现象:在基于 Alpine 的 Docker 镜像里执行aapt2 version,报No such file or directory,但文件明明存在。

原因:build-tools 29.0.2 里的 aapt2 是动态链接到 glibc 的,而 Alpine 用的是 musl libc。两者不兼容。

解决:换用基于 Ubuntu 或 Debian 的基础镜像,或者在 Alpine 里安装gcompat兼容层。但更稳妥的做法是直接用openjdk:8-jdk或ubuntu:18.04作为构建镜像的基础。这个问题在 CI 里非常隐蔽,因为报错信息不会直接说“libc 不匹配”。

4.4 d8 处理 Lambda 表达式时脱糖失败

现象:编译时报Invoke-customs are only supported starting with Android O,但你的minSdkVersion明明设的是 21。

原因:d8 在 29.0.2 里对 Lambda 的脱糖需要显式开启。AGP 默认会处理,但手动调 d8 时如果不加参数,脱糖不会自动生效。

解决:在 d8 命令里加上--min-api 21参数:

$BT/d8 --output ./dex_output/ --lib $PLATFORM --min-api 21 ./classes/**/*.class

--min-api告诉 d8 目标最低 API 级别,它会根据这个值决定是否对 Lambda 和接口默认方法做脱糖。不传这个参数时,d8 默认按最高 API 处理,导致低版本设备上崩溃。

4.5 zipalign 对齐后签名导致对齐失效

现象:先用 zipalign 对齐,再用 apksigner 签名,安装到设备上报INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION。

原因:apksigner 在签名时会修改 APK 的二进制结构,如果先对齐后签名,签名操作可能破坏对齐。正确的顺序是先签名后对齐,或者用 apksigner 的--alignment参数一步完成。

解决:调整顺序,先签名再对齐:

# 先签名 $BT/apksigner sign --ks my-release-key.jks --ks-key-alias mykey \ --out app_signed.apk app_unsigned.apk # 再对齐 $BT/zipalign -v 4 app_signed.apk app_final.apk

但注意,zipalign 在 29.0.2 里对已签名 APK 做对齐时不会破坏签名,因为签名块在 APK 末尾,对齐只影响未压缩数据区。如果还是不放心,用apksigner verify -v app_final.apk验证签名完整性。

5. 验证 build-tools 29.0.2 是否真正可用的三个硬指标

5.1 用 aapt2 dump 检查资源链接结果

编译打包完成后,不要只看 APK 能不能装。用 aapt2 dump 把资源表打出来,确认关键资源都在:

# 查看 APK 的资源表 $BT/aapt2 dump resources app_final.apk | head -50 # 查看 APK 的 badging 信息 $BT/aapt2 dump badging app_final.apk # 参数说明: # dump resources 输出所有资源 ID 和对应的文件路径 # dump badging 输出包名、版本号、权限、启动 Activity # 如果 badging 输出里 launchable-activity 为空,说明 manifest 配置有问题

dump badging的输出里重点看三行:package: name=确认包名,launchable-activity: name=确认入口 Activity,sdkVersion:和targetSdkVersion:确认版本号。如果这三项都对,说明资源链接和 manifest 合并没问题。

5.2 用 apksigner verify 确认签名链完整

签名不是签完就完事,要验证签名是否覆盖了所有文件:

# 验证签名 $BT/apksigner verify --verbose --print-certs app_final.apk # 参数说明: # --verbose 输出每个签名者的详细信息 # --print-certs 打印证书指纹和有效期 # 输出里会列出 v1、v2、v3 签名方案是否通过

29.0.2 对应的 apksigner 支持 v1 和 v2 签名。如果输出里Verified using v1 scheme: true和Verified using v2 scheme: true都是 true,说明签名完整。如果 v2 是 false,检查是否用了--v2-signing-enabled参数,默认是开启的。

5.3 用 dexdump 检查 dex 文件的方法数

方法数超限是 Android 构建的经典问题,29.0.2 里可以用 dexdump 快速统计:

# 统计 dex 中的方法数 $BT/dexdump -f app_final.apk | grep "method_ids_size" # 输出示例:method_ids_size : 65536 # 如果接近 65536,说明需要开启 multidex

method_ids_size超过 65536 时,d8 会报错。解决办法是在build.gradle里开启multiDexEnabled true,并且让 Application 类继承MultiDexApplication。但注意,multidex 在 API 21 以上是原生支持的,21 以下需要引入 support 库。

我自己的习惯是每次离线部署完 build-tools 之后,先跑一遍aapt2 version、d8 --version、apksigner version三个命令,确认工具链完整。然后拿一个最小的 Hello World 项目走一遍完整链路,从 aapt2 compile 到 apksigner sign,全部通过之后再接入正式项目。这个习惯帮我省过很多次“以为环境好了结果编译到一半崩掉”的时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询