简介:Android命令行工具(commandlinetools-mac-11076708-latest.zip)是官方发布的macOS版SDK管理套件,专为不安装完整Android Studio、需要通过命令行完成Android SDK下载与构建的开发者设计。整个压缩包共104个文件,以93个jar依赖库为主,并集成sdkmanager、avdmanager、apkanalyzer、lint、retrace、screenshot2等可执行工具,还包含properties配置文件和readme说明文档,压缩后体积约146.49MB。目前已有201人学习/下载。借助sdkmanager可以灵活下载和管理多版本SDK组件,avdmanager负责创建与管理Android虚拟设备,apkanalyzer用于解析APK文件,lint则能检查代码中的潜在问题。这套工具链覆盖了从环境准备、依赖管理到应用分析的常见流程,适合已有命令行使用习惯的Android开发者,也是理解Android SDK运行原理的实用资料。
1. 在 macOS 上只靠一个命令行工具包拉起 Android 构建环境:先认识 commandlinetools-mac-11076708-latest.zip
如果你接到的任务是把一台全新的 Mac 变成 Android 构建机,而不是写界面,那么任何带图形界面的 IDE 都会显得笨拙又占地方。我第一次在测试机配 CI 镜像时就吃过这样的亏:装完 IDE、再让它自动补全 SDK 组件,磁盘少了十几个 GB,而且没人敢动它的配置。后来换成下载 commandlinetools-mac-11076708-latest.zip 这个命令行工具包,才开始觉得 Android 的构建链是可控的。这个压缩包本身不大,解压后是一个 cmdline-tools 目录,里面只提供 sdkmanager、avdmanager、apkanalyzer 这类命令行入口;真正干活的 platform-tools、build-tools、platform、system-image,都要靠这些命令去拉。适合三类人:要脚本化打包的开发者、要同时管理多台苹果电脑构建机的人,以及想搞懂 Android SDK 目录结构的人。
2. 把 commandlinetools-mac 装成 SDK 根目录:解压、目录布局与三个环境变量
这个 zip 最大的特点是“解压就能用”但“放错地方就用不了”。很多人拿到压缩包直接双击解压到下载目录,然后发现 sdkmanager 报错,或者把包装到了奇怪的位置。问题不在命令本身,而在命令行工具在 SDK 根目录里的相对位置。macOS 上要从零把 SDK 配好,需要做三件事:确认前置环境、按约定摆放 cmdline-tools、配置好 shell 变量。
2.1 下载前先看架构和 Java:mac 上装命令行工具最容易忽略的两个前置检查
命令行工具本身是 Java 程序,大部分逻辑跟 CPU 架构无关,但你后续用 avdmanager 下载模拟器镜像时必须知道这台机器是 arm64 还是 x86_64。mac 上 Apple Silicon 与 Intel 的镜像 ABI 不通用,选错镜像轻则创建失败,重则模拟器启动后黑屏。先执行两条命令把环境底细记录下来。
uname -m java -version第一条命令输出 arm64 表示这是 Apple Silicon 机器,后续模拟器镜像要选 arm64-v8a;输出 x86_64 就选 x86_64。第二条命令检查 JDK 版本,高版本的命令行工具通常要求 Java 17 或更高。如果 java -version 报找不到命令,或者版本停留在 8、11,后面跑 sdkmanager 会直接在启动阶段崩掉。系统没装 Java 时,常见做法是用包管理器装 openjdk@17,或者复用 IDE 自带的 Java 运行时目录。装完之后重新打开一个终端窗口,再跑一遍 java -version,直到看到 17 以上的版本号再做下一步。这个检查只花一分钟,却能省掉后面最莫名其妙的一次排错。还有一个容易被忽略的点:文件名里的 11076708 是这个工具包的构建编号,官方会随发布周期换新编号。不要把这个数字写死在长期脚本里,脚本只该关心它拿到的是哪一个压缩包,否则这次用的编号在下个季度很可能就装不到新发布的平台组件。
2.2 解压后必须把 cmdline-tools 放到 latest 子目录,否则 sdkmanager 找不到自己
zip 解压后得到的顶层目录叫 cmdline-tools,里面是 bin 和 lib。sdkmanager 的默认行为是从自身执行路径反推 SDK 根目录,它约定根目录下必须存在 cmdline-tools/latest/bin。也就是说,正确结构是 $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager。如果只把解压目录丢到 SDK 根目录下,路径会变成 $ANDROID_HOME/cmdline-tools/bin/sdkmanager,运行时它找不到合法的根目录,会把当前目录当成根,后面安装的所有组件都落到错误位置。这是整个 mac 命令行工具安装里最常见的坑。
我一般会先把压缩包解压到临时目录,再整体移动成 latest,这样无论 zip 内部结构是否变化,最终目标唯一。
ANDROID_SDK_ROOT="$HOME/Android/sdk" TOOLS_ZIP="$HOME/Downloads/commandlinetools-mac-11076708-latest.zip" TMP_DIR="$(mktemp -d)" unzip -q "$TOOLS_ZIP" -d "$TMP_DIR" mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools" mv "$TMP_DIR/cmdline-tools" "$ANDROID_SDK_ROOT/cmdline-tools/latest" rm -rf "$TMP_DIR"这段命令先用 mktemp 创建临时目录,避免解压文件散落在当前目录;unzip 加上 -q 参数只输出错误信息,干净一些。mkdir -p 确保 SDK 根下的 cmdline-tools 存在,然后把临时目录里的 cmdline-tools 整体改名成 latest。如果你重复执行这段脚本,需要对原来的 latest 做备份或删除,否则 mv 会把旧目录嵌套成 latest/cmdline-tools,目录叠套的坑就是这么来的。rm -rf 清理临时目录是为了不让副本留在磁盘上。完成后最好检查一下最终路径:ls "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager",能看到这个文件再继续。
2.3 PATH、ANDROID_HOME、ANDROID_SDK_ROOT 一次配齐
目录放好之后,还需要让终端能找到 sdkmanager、avdmanager、adb 和 emulator。macOS 的 zsh 默认读 ~/.zshrc,如果你的构建机登录 shell 是 bash,就得写进 ~/.bash_profile。我倾向于只改当前用户的 rc 文件,不改 /etc/paths,避免影响机器上其他账号。
export ANDROID_HOME="$HOME/Android/sdk" export ANDROID_SDK_ROOT="$ANDROID_HOME" export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH"ANDROID_HOME 是早期工具链就在用的变量,ANDROID_SDK_ROOT 是新版构建系统更认的名字,两个都设能兼容更多场景。PATH 的顺序有讲究,cmdline-tools/latest/bin 必须放在最前面,避免 /usr/local/bin 或其他目录里存在同名命令时抢掉版本。platform-tools 和 emulator 目录此刻可能还不存在,这不影响 shell,PATH 里的无效目录会被忽略,等后续组件装完就能直接使用。配置完成后执行 source ~/.zshrc,再运行 sdkmanager --version,能打印出版本号就说明这一层已经通了。如果提示 command not found,优先看变量的拼写和当前 shell 环境;如果打印 Java 报错,回到 2.1 检查 JDK。注意不要用 sudo 去跑 sdkmanager,尤其是 SDK 目录建在自己用户目录下时,sudo 会把 licenses、临时文件变成 root 所有,之后普通用户再写就会遇到权限拒绝。
3. 用 sdkmanager 把真正干活的东西装进来:platform-tools、platforms、build-tools 的最小命令
cmdline-tools 本身不包含 adb、aapt2、android.jar,它更像一个管家。你要做的下一步是告诉它需要哪些组件。这里最容易踩的误区是以为装了命令行工具就等于装了 SDK,实际上跑构建时缺少 platform-tools,Gradle 会一直报找不到 SDK;缺少 build-tools,aapt2 无法处理资源。所以这一章把包名规则、最小安装命令、许可证处理都讲清楚。
3.1 sdkmanager 的包名规则:分号后面是什么,cat 输出怎么看
运行 sdkmanager --list 会输出很长的组件列表,第一次看容易晕。列表里每个包名都带分号,比如 platforms;android-34、build-tools;34.0.0、system-images;android-34;google_apis;arm64-v8a。这里的分号表示分类层次,不是文件路径分隔符。在 shell 里必须用双引号把包名包起来,否则某些环境下分号会被当成命令分隔符,命令执行逻辑完全变样。
常用组件可以大致分成五类,表格里列的是最常遇到的包名。
| 包名 | 实际包含什么 | 什么时候需要 |
|---|---|---|
| platform-tools | adb、fastboot、mke2fs 等 | 任何设备交互、调试、安装 APK |
| platforms;android-34 | android.jar 和平台元数据 | Gradle 编译时必须匹配 compileSdk |
| build-tools;34.0.0 | aapt2、aidl、zipalign 等 | 手动做资源编译、签名对齐时使用 |
| emulator | 模拟器可执行文件 | 需要跑虚拟设备时 |
| system-images;android-34;google_apis;arm64-v8a | 模拟器系统镜像 | 创建 AVD 的前置条件 |
还有 cmdline-tools;latest 这个包,它是命令行工具自更新用的。原则上你不需要刻意安装,但如果看到 sdkmanager 提示自身版本较老,用这个包名可以拉取更新。这些组件占用从几百 MB 到几个 GB 不等,所以在装之前要明确目标。如果只是命令行出包,emulator 和 system-images 可以先不装;要做 UI 自动化再补。这也是命令行方式比图形 IDE 更省磁盘的原因之一,按需取用。
3.2 最小安装命令:--sdk_root、--channel 与版本配套
装一组最常用的组件,命令可以写成下面这样。推荐把 --sdk_root 显式写出来,这样即使当前 shell 环境变量被其他脚本覆盖,也仍然装到预期位置。
sdkmanager --sdk_root="$ANDROID_HOME" \ "platform-tools" \ "platforms;android-34" \ "build-tools;34.0.0" \ "emulator"platform-tools 提供 adb;platforms;android-34 提供 android.jar,Gradle 的 compileSdk 为 34 时必须存在;build-tools;34.0.0 里包含 aapt2 等资源处理工具,虽然 Gradle 也能自动下载构建工具,但手动装到一个固定版本更可控;emulator 是模拟器本体,不在 commandlinetools 范围内,需要单独安装。包名里的版本号是有配套关系的,platforms;android-34 通常对应 build-tools;34.0.0,你用 33 的 platform 配 34 的 build-tools 也能跑,但没必要。--channel 参数控制预览版渠道,0 是正式版,1 是 beta,2 是 alpha,3 是 canary。日常构建用 0 就够了,尝鲜新 API 才需要调到 2 或 3。预览版可能伴随不兼容行为,不建议把它写进正式构建脚本。安装过程中需要从网络拉取文件,中途不要随意按 Ctrl-C,否则会出现半安装状态:licenses 目录写入了,但组件文件不完整,重新安装时又可能因为目录已存在而跳过覆盖一部分内容。
3.3 许可证不通过怎么办:yes 管道与 --licenses 的适用场景
第一次安装带许可的组件时,命令行工具会打印一长段协议,然后停在那里等输入 y 或 n。人工操作没问题,自动化流水线必须提前处理。最简单的方案是接一个 yes 管道。
yes | sdkmanager --sdk_root="$ANDROID_HOME" "platforms;android-34"yes 会不断输出 y,当命令读完需要的输入后管道关闭,进程自然继续。适合只装一两个包的情况。如果需要同时装很多包,每个包都去接受一遍很烦,更值得统一处理。
yes | sdkmanager --licenses --sdk_root="$ANDROID_HOME"这个命令会扫描 SDK 根目录下所有已安装但还未接受许可的组件,统一补齐。许可接受状态记录在 SDK 目录下的 licenses 文件夹里,同一类许可只要接受过一次,后续再装不会重复询问。注意安装时不要用 sudo,如果已经用了,检查 licenses 目录是否属于当前用户,必要时 chown 修正,否则后续安装会一直提示权限不足。
3.4 把 SDK 装进项目目录还是固定目录:我如何选
默认情况下,我会把 SDK 放在 $HOME/Android/sdk 下,所有项目共享一份,省空间,环境变量也好统一。但有些场景需要做到“项目私有”,比如离线构建、多个客户要求不同 API 版本,或者你想验证某一次构建在全新 SDK 下也能复现。这时可以把 SDK 放到项目里的隐藏目录。
sdkmanager --sdk_root="$PWD/.android-sdk" \ "platform-tools" "platforms;android-34" "build-tools;34.0.0"然后在项目 local.properties 里写上 sdk.dir 指向这个目录。要注意 sdk.dir 必须是绝对路径,相对路径在 Gradle 解析时会出问题。项目私有 SDK 最大的缺点是体积,一个最小的工具链至少三四个 GB,多几个项目就会把磁盘吃光。所以我的选择标准是:共享机器用固定目录,临时实验或合规要求严格的构建环境才放项目目录。如果你把 local.properties 提交到版本库,不同开发者路径不一致会立刻翻车,正确的做法是把它加入忽略列表,只在本地生成。
4. avdmanager 建模拟器:选镜像、写 device 配置、等待 sys.boot_completed
有了平台和构建工具,命令行出包已经能跑通。但做 UI 测试、截图回归、动态授权测试时,还需要一台命令行能创建和启动的模拟器。avdmanager 是命令行工具里专门负责 AVD 的部分,它可以做到和图形界面一样的效果,而且每一步都可脚本化、可回放。这章从装镜像开始,把创建到启动完整走一遍。
4.1 先用 sdkmanager 装对 ABI 的系统镜像
AVD 的核心是系统镜像,所以在执行 avdmanager 之前,必须先安装对应的 system-images。镜像包名结构是 system-images;android-API;variant;abi,比如 system-images;android-34;google_apis;arm64-v8a。API 决定系统版本,variant 通常是 google_apis 或 default,abi 必须和宿主机架构一致。Apple Silicon 机器选 arm64-v8a,Intel 机器选 x86_64。选错 ABI 不会在安装时报错,但会在启动模拟器时出现各种诡异问题,比如黑屏、CPU 不支持、进程反复崩溃。
sdkmanager --sdk_root="$ANDROID_HOME" \ "system-images;android-34;google_apis;arm64-v8a"执行完后,镜像会被放到 SDK 根目录下对应的 system-images/android-34/google_apis/arm64-v8a 目录。avdmanager 创建 AVD 时会读取这个路径,所以如果镜像安装到了 A 目录,而 ANDROID_SDK_ROOT 指向 B 目录,结果就是找不到镜像。每次在脚本里创建 AVD 之前,确认环境变量和 sdkmanager 使用同一个根目录,这个检查只需要一条命令:echo $ANDROID_SDK_ROOT。
4.2 create avd 的参数和 echo no 的坑
镜像就绪后,创建 AVD 的最小命令如下。
printf 'no\n' | avdmanager create avd \ -n ci_avd \ -k "system-images;android-34;google_apis;arm64-v8a" \ --device "pixel_6" \ --force-n 指定 AVD 名称,后续启动用这个名称找到设备;-k 要填完整的镜像包名,必须与已安装镜像完全一致;--device 指定硬件模板,pixel_6 提供一组默认分辨率、density 和外形参数;--force 表示同名 AVD 存在时直接覆盖,这在反复跑脚本时很有用。printf 'no\n' 回答的是创建过程中那个交互式问题:“Do you wish to customize the hardware profile? [no]”。很多人直接不加,结果脚本卡在交互等待上。不使用 yes 管道的原因很简单,create avd 只期望一行输入,yes 会不断输出,反而可能干扰后续流程。创建完成后可以用下面命令检查。
avdmanager list avd -c这个命令只输出有效 AVD 的配置路径。看到类似 ~/.android/avd/ci_avd.avd 的结果,说明创建成功。如果列表为空,多半是系统镜像没有安装,或者环境变量没有指到同一个 SDK 根目录。
4.3 启动模拟器与等待开机完成的轮询写法
创建好 AVD 只是生成配置文件,真正的模拟器进程由 emulator 命令启动。在无显示器或 SSH 场景下,需要加无头参数。
"$ANDROID_HOME/emulator/emulator" -avd ci_avd \ -no-window -no-audio -no-boot-anim -no-snapshot &-no-window 关闭窗口,适合 CI;-no-audio 去掉音频设备;-no-boot-anim 跳过开机动画,缩短冷启动时间;-no-snapshot 不使用快照恢复,保证每次从干净状态启动,避免历史状态污染测试。模拟器进程在后台运行,调试时可以把这些参数去掉,留下 -avd ci_avd 就能看到图形窗口。设备在线和系统启动完成是两回事,adb wait-for-device 只代表设备被 adb 识别,不代表可以开始操作。要等系统真正起来,需要轮询 getprop。
export PATH="$ANDROID_HOME/platform-tools:$PATH" adb wait-for-device until [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ]; do sleep 2 doneadb shell getprop sys.boot_completed 在系统未完成启动时返回空字符串,完成后返回 1。tr -d '\r' 是为了去掉 Windows 风格的回车符,macOS 下一般没有,但脚本复制到 Windows 环境时这行能避免奇怪的字符串比较问题。轮询间隔设 2 秒比较合理,太快会频繁拉起 adb 进程,太慢则会拖慢整个构建流程。如果 3 分钟内还没完成,说明模拟器可能陷入重启循环,需要回头检查配置。
4.4 基于 config.ini 调分辨率、内存和 CPU 核数
创建 AVD 时使用的硬件模板决定了初始的屏幕尺寸和内存,但实际测试时往往要按需调整。AVD 的配置文件在 ~/.android/avd/<名称>.avd/config.ini,模拟器关闭状态下可以手动修改。常见改动是下面几项。
hw.ramSize = 2048 hw.cpu.ncore = 4 hw.lcd.width = 1080 hw.lcd.height = 2340hw.ramSize 单位是 MB,2048 表示给模拟器 2GB 内存,这个值不是越大越好,macOS 本身也要内存,设置过大会拖慢宿主机。hw.cpu.ncore 控制模拟器可用 CPU 核数,设成 4 基本够用,除非宿主机核数很多且确有余量。分辨率修改后,渲染压力会明显变化,无头模式下影响不大,有窗口的本地调试会变卡。特别注意,修改 config.ini 前一定要确认模拟器进程已退出,否则退出时模拟器会把内存中的旧配置写回来,覆盖你的修改。
5. mac 命令行工具包使用排查:五个经典翻车点的现象、原因、解决
即便前面章节全部照做,实际运行一周后仍可能遇到问题。有些问题只出现在特定机器上,有些则出现在重装或升级之后。下面五条是高频率翻车现场,按现象到原因再到解决的顺序写,可以直接对照处理。
5.1 现象:sdkmanager 报 java.lang.NoClassDefFoundError
新装的命令行工具一执行就在启动阶段失败,错误提示里有 java.lang.NoClassDefFoundError,有时还提到 javax/xml/bind/JAXBException。原因是机器默认的 Java 版本太老,高版本命令行工具需要的模块在 Java 8 或 11 里不存在。macOS 上如果同时存在系统自带 Java 和后来安装的 JDK,很容易出现默认版本不对。解决方法是先检查当前 shell 的 JVM 版本:java -version。如果版本低于 17,安装新版 JDK 后,把 JAVA_HOME 指到新 JDK 的目录。如果不想额外装 Java,可以复用某个 IDE 运行时,把 JAVA_HOME 指向 IDE 安装包内的 jbr 目录。改完环境变量后要重新打开终端,因为当前 shell 的变量不会自动刷新。这个问题最气人的地方在于它不影响 IDE 内的构建,只在纯命令行环境下爆炸,所以排查时第一反应往往不对。
5.2 现象:platform-tools 装完,adb 仍然 command not found
sdkmanager 明确显示 platform-tools 安装成功,但新终端里运行 adb 却提示找不到。原因通常是 PATH 里没有加入 $ANDROID_HOME/platform-tools,或者环境变量里 SDK 路径与组件实际安装路径不一致。先执行 ls "$ANDROID_HOME/platform-tools/adb" 确认文件是否存在,如果文件在,就检查 shell 配置里 PATH 是否包含这个目录;如果文件不在,看是否因为安装时使用了 --sdk_root 指向了别的位置。还有一种少见的情况是用户把 SDK 放在云同步目录或网络盘里,adb 这种需要可执行权限和局部 socket 文件的工具在这种文件系统上会失效,把 SDK 移到本地普通目录再试。
5.3 现象:avdmanager create avd 在交互提示卡住
脚本执行到 create avd 时一直停着,没有报错,也没有输出,CI 任务直到超时。原因是 create avd 会从标准输入读取“是否自定义硬件配置文件”的答复,脚本没有给它任何输入,它就一直等待。解决方法是显式给一行输入。推荐使用 printf 'no\n' 而不是 echo no,两者行为接近,但 printf 不依赖 shell 别名,在严格模式下更可控。如果这条命令已经被包装到其他函数里,确认标准输入没有被重定向到 /dev/null,否则 printf 的信息也不会被命令读到。在 CI 的 YAML 配置里,我会把整条命令写成一个脚本步骤,而不是拆成多条 shell 字符串,避免管道的解析差异。
5.4 现象:模拟器黑屏、boot_completed 一直不返回
模拟器进程在运行,adb devices 也能看到设备,但 sys.boot_completed 一直不是 1,或者屏幕长时间黑着。最常见的原因是镜像 ABI 与宿主机不匹配,Intel 机器上装了 arm64-v8a 镜像,运行时靠二进制转换,性能极差且容易卡死。其次是配置的内存过低,系统反复重启。解决方法是先确认 uname -m 的输出与 system-images 包名中的 ABI 是否一致,再检查 config.ini 的 hw.ramSize 是否低于 1536。把内存调到 2048,核数至少 2,加上 -gpu swiftshader_indirect 参数,很多渲染相关的问题能缓解。另外要检查后台是否残留了多个模拟器进程,多个实例同时启动会抢占资源,先全部杀掉再重启。
5.5 现象:旧版 zip 的编号过期后,装不了新 API
某个时间点之后,sdkmanager --list 里看不到新发布的 API 组件,或者安装某个新 platform 时报 404。原因是命令行工具包本身是一个固定的构建版本,随着时间推移,新组件的索引不会主动进入旧版工具。解决方法是更新命令行工具本身。可以用 sdkmanager "cmdline-tools;latest" 自更新,也可以直接重新下载新的 mac 版压缩包,按第 2 章的流程覆盖 latest 目录。我更倾向于重新下载压缩包,因为能顺势确认压缩包的目录结构和权限。覆盖前先备份旧的 latest,新工具验证无误后再删除备份,这个习惯能避免更新后工具不能用的尴尬。
6. 把整套流程固化成一条命令:从 unzip 到模拟器 boot 完成的初始化脚本
前面所有的步骤,最终都应该变成一条能重复执行的命令。下面这段脚本把下载好的压缩包路径作为唯一入参,完成解压、摆放、安装组件、创建 AVD、启动并等待开机完成。适合放在新机器的初始化任务里。
6.1 一个可重复执行的 bash 脚本
#!/bin/bash set -euo pipefail TOOLS_ZIP="${1:?请提供 commandlinetools zip 路径}" SDK_DIR="${ANDROID_HOME:-$HOME/Android/sdk}" TMP="$(mktemp -d)" unzip -q "$TOOLS_ZIP" -d "$TMP" mkdir -p "$SDK_DIR/cmdline-tools" rm -rf "$SDK_DIR/cmdline-tools/latest" mv "$TMP/cmdline-tools" "$SDK_DIR/cmdline-tools/latest" rm -rf "$TMP" export ANDROID_HOME="$SDK_DIR" export ANDROID_SDK_ROOT="$SDK_DIR" export PATH="$SDK_DIR/cmdline-tools/latest/bin:$SDK_DIR/platform-tools:$SDK_DIR/emulator:$PATH" yes | sdkmanager --sdk_root="$SDK_DIR" \ "platform-tools" \ "platforms;android-34" \ "build-tools;34.0.0" \ "emulator" sdkmanager --sdk_root="$SDK_DIR" \ "system-images;android-34;google_apis;arm64-v8a" printf 'no\n' | avdmanager create avd \ -n ci_avd \ -k "system-images;android-34;google_apis;arm64-v8a" \ --force "$SDK_DIR/emulator/emulator" -avd ci_avd -no-window -no-audio -no-boot-anim -no-snapshot & adb wait-for-device for i in $(seq 1 60); do if [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ]; then echo "boot completed" exit 0 fi sleep 2 done echo "boot timeout" >&2 exit 1set -euo pipefail 的作用是遇到未定义变量、命令失败或管道中任一环节出错时立即退出,避免 CI 误判。rm -rf 旧的 latest 让脚本可重复执行,这是我在第一次目录叠套翻车后加进去的。yes 管道负责统一接收所有许可,printf 只回答 create avd 的一个问题,for 循环给开机等待加了 120 秒上限,超时直接退出并返回错误码,不会让任务无限挂起。如果你用的是 Intel Mac,把 system-images 包名里的 arm64-v8a 改成 x86_64 再执行。
6.2 跑完以后的验证与软收尾
模拟器启动完成后,先确认设备状态,再执行分辨率检查。
adb devices adb shell wm size adb emu killwm size 输出的结果应该与 config.ini 里设置的分辨率一致,如果不一致,说明模拟器进程启动时可能读取了旧配置,需要关闭后检查配置。adb emu kill 是优雅关闭模拟器的命令,比直接 kill 进程更可靠,直接杀进程有时会留下锁文件,导致下次启动报“another emulator instance is running”。
整套脚本第一次执行大约需要下载几个 GB 组件,磁盘不够时最容易卡在 system-images 那一步。中断后不要直接重跑,先看 SDK 目录里有没有半包,有就删掉对应目录再跑。我在一次现场演示前没清理旧 latest,解压后目录叠套,导致 avdmanager 用旧版本工具报了一堆不可名状的问题。后来把“先删再 mv”固定进脚本,再也没翻过车。把这个顺序当成默认习惯,能帮你省下不少排错时间。希望帮到你。
本文还有配套的精品资源,点击获取