macOS上使用Android命令行工具:不装IDE也能玩转SDK与APK
简介:面向无需完整安装 Android Studio 的 macOS 开发者,这份官方命令行工具包提供基于终端完成 SDK 管理与构建分析的核心能力,适用于 CI/CD 自动化、远程开发或轻量环境配置等场景。工具包共 104 个文件,压缩后约 146.49MB,内容以 93 个 Java 归档(jar)为主体,为各命令提供运行时依赖;同时包含 sdkmanager、avdmanager、apkanalyzer、lint、profgen、screenshot2 等可执行脚本,分别覆盖 SDK 组件下载与更新、虚拟设备管理、APK 信息解析、代码静态检查、性能配置生成和界面截图等任务,基本形成一条完整的命令行工具链。另有属性配置、说明文档等辅助文件,便于开发者快速了解各项命令的用途与调用方式。目前已有 200 人学习下载,适合希望摆脱 IDE 束缚、在纯终端下搭建 Android 构建环境的开发者,也可直接嵌入脚本完成批量 SDK 组件安装与产物分析,显著提升自动化效率。
1. macOS 上的 Android 命令行工具:不装 IDE 也能跑通 SDK 全流程
如果你和我一样,在 mac 上只想要一个轻量的 Android 开发环境,或者要给 CI 机器配一套不带图形界面的构建工具链,那么 Android 命令行工具(commandlinetools-mac-11076708-latest.zip)就是那包"后悔药"。它本质上是 Android Studio 里那套 SDK 管理能力的命令行版本,解压后就能拿到 sdkmanager、apkanalyzer、avdmanager、lint 这些真正干活的命令。不需要安装几个 GB 的 IDE,不需要反复点向导,一个 zip 解压、配好 JAVA_HOME,就能把 SDK 平台包、构建工具、模拟器镜像全部拉下来。这篇笔记从我实际拆包和配置的经历出发,把每个工具的用法、参数和我在 mac 上踩过的坑一次说清楚。
2. 装好第一步:sdkmanager 与命令行环境配置
2.1 解压与目录结构:bin 下的命令和 lib 下的 jar 各管什么
下载下来的 zip 包我一般放在~/android/cmdline-tools下,解压后第一件事是看目录结构。bin/目录里躺着sdkmanager、apkanalyzer、avdmanager、lint这几个可执行脚本,真正的实现都在lib/目录的一堆 jar 里。这也是命令行工具和 Android Studio 最大的差别:没有 IDE 外壳,所有功能都是脚本加 Java 进程直接调用。
lib/下的 jar 不是摆设,每个都对应一块能力。r8.jar是代码缩减和混淆引擎,kotlin-compiler-mvn.jar和kotlin-reflect-1.9.0.jar说明工具链里内置了 Kotlin 编译与反射支持,tools.lint-checks.jar是 lint 静态检查的检查器集合,proto.jar、bcprov-jdk15on-1.67.jar、guava-31.1-jre.jar则是序列化、加密算法和通用工具库的运行时依赖。也就是说,这套工具内部自己就是一套完整的 Java 应用集群,只是把入口暴露成了命令行脚本。
| 文件 | 在工具链里的角色 |
|---|---|
| sdkmanager | 管理 SDK 平台、构建工具、系统镜像的下载与卸载 |
| apkanalyzer | 解析 APK,读取 manifest、dex、资源、文件大小 |
| avdmanager | 创建和管理 Android 虚拟设备(AVD) |
| lint | 静态代码检查,输出 HTML/XML 报告 |
| r8.jar | 代码缩减与混淆引擎,被 Gradle 或命令行调用 |
| tools.lint-checks.jar | lint 的内置检查器实现 |
需要注意一点,这个包解压之后默认不会自动出现在PATH里,而且不同版本的命令行工具对目录名有要求。新版工具会从cmdline-tools/latest/bin下读取命令,所以我解压后会把文件夹重命名成latest,再在环境变量里指过去。maven 配环境变量那套经验在这里不适用,因为 SDK 的命令行工具不依赖ANDROID_SDK_ROOT去定位自身,它只要求你知道自己把 zip 放哪了。
2.2 PATH 与 JDK 17:mac 上最容易翻车的一环
命令行工具是 Java 应用,所以 JDK 是第一道门槛。这个版本的 commandlinetools 需要 JDK 17,如果你的机器上只有一个老版本的 JDK 8 或 11,运行sdkmanager --list时会直接报类找不到或者版本不支持的错。我一般用 Homebrew 装 Zulu 17,装完之后用/usr/libexec/java_home -v 17拿路径,这个命令是 mac 上定位 Java 版本最省事的办法。
# 在 ~/.zshrc 里追加以下内容 export JAVA_HOME=$(/usr/libexec/java_home -v 17) export ANDROID_HOME=$HOME/android/sdk export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin source ~/.zshrc配置逻辑分三段:JAVA_HOME解决 Java 运行时来源,ANDROID_HOME是 SDK 包的安装根目录,PATH把 bin 下的四个命令暴露给终端。值得强调的是,Android Studio 默认把 SDK 装在~/Library/Android/sdk,但命令行工具并不强制要求这个路径,你可以自定义到任意目录,只要后续所有工具都引用同一个ANDROID_HOME就行。
配置完成后,用java -version和which sdkmanager各验证一次。很多 mac 用户在这步翻车,是因为把配置写进了.bash_profile,但系统默认 shell 是 zsh,根本不读这个文件。我在第 4 章避坑部分会再展开讲这个问题。
2.3 sdkmanager 参数详解:装包、看版本、签协议
sdkmanager 是整个工具链的入口,几乎所有 SDK 组件都由它下载。第一次运行先执行--list看看远端有哪些包,这一步同时会验证 JDK 环境是否正常。列表输出很长,我一般会 grep 关键字,比如只看平台包就用sdkmanager --list | grep "platforms;"。
# 列出所有可用包 sdkmanager --list # 安装平台、构建工具和平台工具 sdkmanager --install "platforms;android-34" "build-tools;34.0.0" "platform-tools" # 接受所有开源协议的许可 sdkmanager --licenses这里的包名格式是组件的类型;版本号,platforms;android-34代表 Android 14 的 SDK 平台,build-tools;34.0.0是构建工具版本。--licenses是必跑的一步,新装的 SDK 目录里没有任何许可记录,不执行的话后面用到aapt2、zipalign这类工具时会反复弹交互确认,在脚本环境里直接卡死。--install可以同时传多个包名,不要一个个装,速度慢而且容易中途断掉。
装完之后检查目录结构,$ANDROID_HOME下应该出现platforms/android-34、build-tools/34.0.0、platform-tools三个目录。如果之后要用模拟器,还需要再装一个emulator包和对应的系统镜像,这个在下一章的 avdmanager 部分具体说。
3. 四个高频工具实操:apkanalyzer、avdmanager、r8 与 lint 的参数与输出解读
3.1 apkanalyzer:不打开 Android Studio 也能拆 APK
apkanalyzer 是我在 macOS 上用得最频繁的工具,它读 APK 的方式比 IDE 里的 Build > Analyze APK 更直白,适合在 CI 上验证产物。它的大部分子命令针对 APK 的单一维度设计,比如看版本信息和最低支持版本,用apk summary;看 manifest 里的权限、组件,用manifest print;分析方法数和类数,用dex packages。
# 查看 APK 的基础信息:版本号、SDK 版本、包名 apkanalyzer apk summary app-release.apk # 打印 AndroidManifest.xml 内容 apkanalyzer manifest print app-release.apk # 列出 dex 中的所有类、方法数,判断是否接近 64K 方法数上限 apkanalyzer dex packages app-release.apkapk summary的输出会直接给出packageName、versionName、minSdkVersion、targetSdkVersion这几个字段,适合做产物校验脚本的输入。manifest print输出的是可读文本,不是二进制 XML,适合快速确认某个权限是不是真的被合并进了最终包。dex packages输出的包名列表可以配合 grep 排查某个第三方 SDK 是否被完整打包进去,比如怀疑推送 SDK 被打进 release 包时,直接apkanalyzer dex packages app-release.apk | grep push,一步定位。区别在于,IDE 里的 APK 分析器只能看一个包,命令行工具可以写进循环批量分析几十个渠道包,这是它真正的价值所在。
参数上需要注意,部分子命令支持--human-readable让大小显示成 KB/MB 而不是字节,写报告脚本时我一般都会加上,避免在脚本里再做一次单位换算。
3.2 avdmanager:命令行创建模拟器
avdmanager 做的事情是创建和管理 Android 虚拟设备,对应 Android Studio 里的 Device Manager。但 IDE 里创建模拟器是图形化点选,命令行下需要用-k参数指定系统镜像的包名,包不存在时它会直接报错而不是自动下载。这个设计经常让人误以为命令写错了,实际上是系统镜像没装。
# 安装一个 arm64 架构的 Android 14 模拟器镜像 sdkmanager --install "system-images;android-34;google_apis;arm64-v8a" # 创建名为 ci_test 的 AVD,使用 Pixel 6 设备定义 avdmanager create avd -n ci_test -k "system-images;android-34;google_apis;arm64-v8a" -d pixel_6 # 列出所有已创建的 AVD avdmanager list avd镜像包名的格式是system-images;<平台版本>;<镜像类型>;<架构>。Apple Silicon 的 mac 选arm64-v8a,Intel 芯片的 mac 选x86_64,选错架构会在启动模拟器时提示 ``HVF无法加载。-d pixel_6指定设备皮肤和分辨率配置,这个参数不是必填,但建议指定,否则默认设备是 1280x720 的小屏,跑起来之后 UI 布局跟真实机型差很远。
创建完 AVD 只是第一步,启动它还需要emulator包。sdkmanager --install "emulator"后,直接执行emulator -avd ci_test就能拉起一个带界面的模拟器窗口。在 CI 环境下可以用-no-window -no-audio让它无头运行,跑 UI 自动化测试时这是个常用组合。
3.3 r8:代码缩减与 keep 规则的写法
r8 是 Android 构建链里的混淆和缩减引擎,在命令行工具包里以lib/r8.jar形式存在。日常开发中它由 Gradle 插件调用,但在命令行工具放这么个 jar,意味着你可以脱离 Gradle 独立给一个 jar 或 APK 做代码缩减。我用它最多的场景是验证混淆规则——某个第三方库的 keep 规则写没写对,不需要跑整个打包流程,直接对产物 jar 执行一遍 r8 就能看出来。
# 把 lib 下所有 jar 拼成 classpath,避免缺依赖 CP=$(ls "$ANDROID_HOME"/cmdline-tools/latest/lib/*.jar | tr '\n' ':') # 对 input.jar 做 release 混淆,输出到 out 目录 java -cp "$CP" com.android.tools.r8.R8 \ --release \ --output out \ --pg-conf proguard-rules.pro \ --lib "$ANDROID_HOME/platforms/android-34/android.jar" \ input.jar--release表示 release 模式,会同时做代码缩减和资源优化;--pg-conf指定混淆规则文件,就是常见的那种-keep规则;--lib指向 android.jar,这是编译期库,r8 需要它来解析 Android API 的类引用,不会被打进输出包。规则文件里最常见的写法是保留某个包下的全部类:
-keep class com.example.demo.** { *; } -dontwarn java.lang.invoke.**-keep后面的类名模式决定了哪些类在混淆后保持原名,反射调用的类、被注解处理的类、native 方法所在的类通常都要 keep。我第一次用 r8 时只写了一个包名,结果运行时框架用反射找类找不到,报ClassNotFoundException,从那以后我都把反射入口列进规则文件再跑一次 r8 验证。这里顺便说一句,lib/里的bcprov-jdk15on-1.67.jar和guava-31.1-jre.jar是 r8 运行时的加密和通用库依赖,如果只把r8.jar放进 classpath,会报NoClassDefFoundError,上面脚本里把整个 lib 目录拼进 classpath 的做法就是为了规避这个问题。
3.4 lint:把静态检查接进打包流程
lint 是 Android 生态里绕不开的静态检查工具,tools.lint-checks.jar提供了检查器的实现。命令行工具自带的 lint 和 Android Studio 里那个 Lint 检查是同一套引擎,不同的是它可以完全脱离 IDE 运行,输出统一的报告文件,适合接进打包流水线。
# 对 app/src 目录做 Correctness 和 Security 检查,输出 HTML 报告 lint --check "Correctness,Security" --html lint-report.html app/src--check后面的参数是检查项 ID,多个用逗号分隔。Correctness覆盖空指针、资源引用错误这类会影响运行的问题,Security覆盖明文流量、不安全的加密算法这类安全短板。--html指定报告输出路径,也可以换成--xml生成机器可读的 XML 报告。app/src是源码目录的路径,lint 会自动识别AndroidManifest.xml和 java/kotlin 源码。
lint 的输出里每个问题会标注严重级别,Error和Warning是最常见的两种。比如检测到ObsoleteSdkInt会给出警告,检测到UnusedResources会给出提示。在脚本里我一般用退出码判断结果:lint 检查到 Error 级别的问题时退出码非零,流水线可以直接在那一环拦截发布。这里有个实用技巧,--check可以重复传参,比如--check Correctness --check Security,比逗号写法更清晰,多个检查项组合时优先用这种方式。
4. 避坑手册:JDK 版本、环境变量与模拟器镜像的五个排查记录
4.1 sdkmanager 报错找不到 JAVA_HOME
现象:在 mac 上刚解压完命令行工具,运行sdkmanager --list直接提示找不到java或报JAVA_HOME为空。
原因:命令行工具是 Java 程序,需要从JAVA_HOME或系统PATH里定位 JVM。mac 自带的java命令可能是 Apple 的旧版本,也可能你只装了 JRE 没装 JDK,而工具要求 JDK 17。
解决:先执行/usr/libexec/java_home -V看机器上有哪些 JDK,如果只有 17 就按 2.2 节的方式设置JAVA_HOME。如果没有 17,用 Homebrew 安装 Zulu 17 后再配置,配置完务必重新打开终端或手动source ~/.zshrc。
4.2 输入 sdkmanager 提示 command not found
现象:环境变量配置写好了,当前终端也能echo $PATH看到cmdline-tools/latest/bin,但新开一个终端窗口就找不到命令。
原因:mac 默认 shell 是 zsh,读的是~/.zshrc,而我在第一次配置时把 export 写进了~/.bash_profile。新开的 zsh 终端根本不会加载 bash 的配置文件。
解决:把配置统一放到~/.zshrc末尾。如果像我一样已经写错了地方,可以直接在.zshrc里加一行source ~/.bash_profile,但更干净的做法是删除重复配置,只保留一份环境变量定义。从那以后我每次改完环境配置都会新开一个终端验证,不直接复用旧会话。
4.3 avdmanager create avd 报 Missing platform
现象:用avdmanager create avd -k "system-images;android-34;google_apis;arm64-v8a"创建 AVD,命令报Error: Missing platform或类似的镜像找不到错误。
原因:system-images和platforms是两个独立的包,只装了platforms;android-34不代表有对应的系统镜像。avdmanager 创建 AVD 时不会自动下载镜像,它只会检查本地是否存在。
解决:先用sdkmanager --list确认system-images;android-34;google_apis;arm64-v8a这个包名完整存在,再执行sdkmanager --install单独安装。装完后再跑一次avdmanager list avd,确认设备被识别,最后用emulator -avd启动验证。
4.4 lint 运行报 SDK 平台缺失
现象:lint 对项目执行检查时,输出类似failed to find target android-34或No such file: android.jar的错误。
原因:lint 解析工程时需要读取编译目标对应的android.jar来建立 API 索引。如果$ANDROID_HOME/platforms/android-34没有安装,它就找不到这个文件。很多人以为 lint 只分析源码,忽略了它对 SDK 平台包的依赖。
解决:检查工程的build.gradle里的compileSdk版本,然后执行sdkmanager --install "platforms;android-<版本号>"安装对应平台。安装完成后再跑 lint,一般就能正常运行。如果还报错,用lint --version确认命令行工具的 lint 版本比工程要求的 AGP lint 版本新。
4.5 macOS 解压后 bin 下的脚本没有执行权限
现象:解压commandlinetools-mac-11076708-latest.zip后,执行sdkmanager提示Permission denied,或者 mac 弹出"无法打开,因为无法验证开发者"的对话框。
原因:zip 包内脚本的 Unix 执行权限在解压时没有被完整保留,或者文件被 macOS 的 Gatekeeper 标记了 quarantine 属性。新版 mac 系统对这个属性尤其敏感。
解决:先给脚本目录补上执行权限,chmod +x "$ANDROID_HOME"/cmdline-tools/latest/bin/*。如果仍然提示开发者验证问题,对命令行工具根目录执行xattr -dr com.apple.quarantine移除隔离属性。这个操作只影响当前目录下的文件,不会改动系统其他位置,安全上没有问题。
5. 进阶技巧:把 lint、r8 和 apkanalyzer 固化成一条 build-check.sh
5.1 一个脚本解决日常重复劳动
命令行工具单独用都不难,难的是把这几个工具串成一条流水线。我现在在 mac 上验证一个新到的 Android 工程,会直接跑一个build-check.sh,把环境检测、平台检查、APK 分析、lint 检查一次做完。这个脚本的思路是:先确认基础环境,再验证工具链,最后输出报告。
#!/bin/bash set -euo pipefail echo "[1/4] 检查环境变量" test -n "$JAVA_HOME" || { echo "JAVA_HOME 未设置"; exit 1; } test -n "$ANDROID_HOME" || { echo "ANDROID_HOME 未设置"; exit 1; } TOOLS_BIN="$ANDROID_HOME/cmdline-tools/latest/bin" echo "[2/4] 检查目标平台" PLATFORM="platforms;android-34" "$TOOLS_BIN/sdkmanager" --list | grep "$PLATFORM" || { "$TOOLS_BIN/sdkmanager" --install "$PLATFORM" } echo "[3/4] 分析 APK 产物" "$TOOLS_BIN/apkanalyzer" apk summary app-release.apk "$TOOLS_BIN/apkanalyzer" dex packages app-release.apk > dex-packages.txt echo "[4/4] 执行 lint 检查" "$TOOLS_BIN/lint" --check "Correctness,Security" --html lint-report.html app/src echo "build-check 完成,报告见 lint-report.html"set -euo pipefail是脚本的保险丝:任何一个命令失败就立即终止,不允许脚本带着错误继续跑。第 2/4 步的 grep 是个小技巧,--list输出里包含目标平台就跳过安装,不包含就自动安装,避免每次重复执行无用的下载动作。第 3/4 步的 apkanalyzer 输出重定向到文件,方便后续脚本继续读取内容做断言。
5.2 用退出码和产物验证工具是否正常
脚本跑完不等于工具链正常,还要看产物。我先检查lint-report.html的文件大小,为空或只有几 KB 说明 lint 大概率报错了;再打开 dex-packages.txt 看方法数和类数是否合理。最直接的办法是把 apkanalyzer 的 summary 输出里几个关键字段抽出来,对比上一个版本的基线值,比如包体大小从 50MB 涨到 80MB,这在合并代码之前就该被发现。
这种把命令行工具串成脚本的方式,比手动逐条执行命令可靠得多。我经历过一次教训:某次升级构建工具版本后,lint 报告生成根本不提示错误但文件为空,我盯着终端看了十分钟才意识到是新版 lint 把输出目录解析成了相对路径,从那以后我每次跑完脚本都会强制检查报告文件的字节数,确认产物真实存在再继续后续步骤。这套 macOS 命令行工具链的价值就在这里,单看每个工具都不复杂,组合起来才是一套可以反复使用的机械化流程。希望帮到你。
本文还有配套的精品资源,点击获取