news 2026/10/22 9:56:53

Linux 下 Android 命令行工具链安装与 CI 构建实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 下 Android 命令行工具链安装与 CI 构建实战

简介:本资源为 Android 命令行工具包 commandlinetools-linux-10406996-latest.zip,面向 Linux 平台下进行 Android 开发、构建与自动化部署的工程师及学习者。若无需安装完整的 Android Studio,可通过该工具集直接获取 sdkmanager 等核心组件,进而下载与管理各类 SDK 包,适用于持续集成环境、服务器端构建以及轻量化开发场景。压缩包共包含 104 个文件,以 93 个 jar 库文件为主体,辅以 sdkmanager、avdmanager、apkanalyzer、lint、retrace、profgen、screenshot2、resourceshrinker 等命令行工具及说明文档,整体约 141.94MB,覆盖 SDK 管理、应用分析、代码检查与资源压缩等常用能力。目前已有 152 人学习下载,适合希望摆脱图形界面、以脚本方式完成 Android 工具链配置的开发者参考使用。

1. 从一个 zip 包说起:命令行工具链在 Linux 上到底装了什么

很多人第一次在 Linux 上配 Android 开发环境,卡住的地方不是写代码,而是拿到一个叫commandlinetools-linux-10406996-latest.zip的压缩包之后不知道下一步该干嘛。这个包是 Android 官方命令行工具集在 Linux 平台上的分发形态,文件名里的10406996是构建编号,latest表示这是当前渠道的最新快照。它不含 Android Studio,也不含模拟器,只有sdkmanager、avdmanager、adb、fastboot等命令行入口,以及一套用于管理 SDK 组件和构建工具的骨架。

它解决的核心问题是:在没有图形界面的服务器、CI 容器、远程开发机上,依然能完整地安装 SDK、接受许可、拉取 platform 和 build-tools,并跑通gradle构建。适合谁?适合需要在 Linux 上做自动化构建、容器镜像制作、或者单纯不想装完整 IDE 的后端和移动端工程师。这个包本身不神秘,但它的目录结构、环境变量和许可机制,是后面所有步骤的地基。

2. 解压之后先别急着用:目录结构与 sdkmanager 的定位逻辑

拿到commandlinetools-linux-10406996-latest.zip之后,第一反应通常是unzip到某个目录,然后直接敲sdkmanager。但如果你不先把目录层级和ANDROID_HOME的关系理清楚,后面会出现「命令找不到」或者「SDK 组件装到了奇怪的地方」这类问题。这一章先把解压后的真实结构和sdkmanager的查找逻辑讲透,再动手。

2.1 解压后的真实目录层级与 cmdline-tools 的嵌套规则

这个 zip 解压出来并不是直接一堆可执行文件,而是一个cmdline-tools目录,里面通常还有一个bin和lib。但 Android 的 SDK 管理器对cmdline-tools的存放位置有明确要求:它必须位于$ANDROID_HOME/cmdline-tools/latest/这种结构下,否则sdkmanager在安装组件时会报「Could not determine SDK root」或者把包装到当前工作目录。

常见做法是手动创建两级目录,把解压出来的内容放到latest下面。下面这段命令是标准操作,注意每一步的路径含义。

# 假设 zip 包放在 ~/downloads 下 mkdir -p ~/android-sdk/cmdline-tools cd ~/android-sdk/cmdline-tools # 解压到临时目录,避免直接铺开 unzip ~/downloads/commandlinetools-linux-10406996-latest.zip -d tmp-extract # 解压后通常得到一个 cmdline-tools 目录,把它重命名为 latest mv tmp-extract/cmdline-tools latest # 清理临时目录 rmdir tmp-extract # 确认结构:latest/bin/sdkmanager 应该存在 ls -l ~/android-sdk/cmdline-tools/latest/bin/sdkmanager

这段逻辑的关键在于:latest这个目录名不是随便起的,sdkmanager内部会通过相对路径向上回溯两级来定位 SDK 根目录。如果你把解压出来的cmdline-tools直接放在~/android-sdk/下,那么sdkmanager会认为 SDK 根目录是~/android-sdk/cmdline-tools的上一级,也就是~/android-sdk,但此时cmdline-tools本身又不在latest里,导致后续安装的 platform 和 build-tools 路径错乱。这个坑我见过不止一次,表现是sdkmanager --list能跑,但sdkmanager "platforms;android-34"装完之后在build.gradle里找不到。

参数说明:-d tmp-extract指定解压目标目录,避免污染当前目录;mv重命名是必须的,因为 zip 内层目录名就是cmdline-tools,不重命名会变成cmdline-tools/cmdline-tools。如果你用unzip -q静默解压,记得检查返回码。

2.2 环境变量怎么设:ANDROID_HOME 与 PATH 的先后顺序

目录结构对了之后,环境变量是第二个容易翻车的地方。很多人只把latest/bin加到PATH,却忘了设ANDROID_HOME,结果sdkmanager虽然能执行,但安装组件时默认往~/Android/Sdk或者当前目录写,和你的预期完全不符。

我一般会在~/.bashrc或~/.zshrc里写三行,顺序不能乱:

# SDK 根目录,指向你创建的 android-sdk 目录 export ANDROID_HOME="$HOME/android-sdk" # 把 cmdline-tools 的 bin 加进 PATH export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$PATH" # 可选:把 platform-tools 也加进去,后面 adb 会用到 export PATH="$ANDROID_HOME/platform-tools:$PATH"

逻辑说明:ANDROID_HOME是给sdkmanager、avdmanager以及后续 Gradle 插件读取的根路径;PATH里的顺序决定了你敲sdkmanager时命中哪个版本。如果你系统里之前装过其他来源的 Android 工具,把latest/bin放在$PATH最前面可以避免版本冲突。改完之后执行source ~/.bashrc,然后用echo $ANDROID_HOME和which sdkmanager双重确认。

这里有个细节:platform-tools目录在你第一次运行sdkmanager安装之前是不存在的,所以那行PATH追加在初始阶段不会报错,但也不会生效,等装完platform-tools之后重新 source 一次即可。不要因为which adb找不到就以为环境坏了。

2.3 用 sdkmanager 安装 platform 与 build-tools 的最小命令集

环境就绪后,先看sdkmanager --list的输出,确认能连上仓库。然后按项目需要安装具体版本。对于大多数现代 Android 项目,platforms;android-34和build-tools;34.0.0是常见组合,但具体版本要看你项目的compileSdk和buildToolsVersion。

# 查看已安装和可用的包 sdkmanager --list # 安装 platform 和 build-tools,注意分号在 shell 里要加引号 sdkmanager "platforms;android-34" "build-tools;34.0.0" # 安装 platform-tools,里面包含 adb 和 fastboot sdkmanager "platform-tools" # 接受所有许可,避免构建时卡在 license 检查 yes | sdkmanager --licenses

逻辑说明:sdkmanager的参数是包名,包名里的分号是 Android SDK 的命名约定,在 shell 中必须用引号包住,否则分号会被解释为命令分隔符。--licenses会逐个询问是否接受,yes |是常见做法,但在 CI 环境里更推荐用sdkmanager --licenses配合expect或者直接写入许可文件。安装完成后,$ANDROID_HOME/platforms/android-34和$ANDROID_HOME/build-tools/34.0.0应该存在。

参数说明:platforms;android-34里的34是 API 级别,对应 Android 14;build-tools;34.0.0是构建工具版本,通常与 API 级别有对应关系,但不必完全一致。如果你不确定项目需要哪个版本,先看项目根目录的build.gradle或build.gradle.kts里的compileSdk和buildToolsVersion。

3. 在 CI 容器里跑通构建:从 zip 到 gradle assemble 的完整链路

本地配好之后,真正的考验是在 CI 容器里复现。容器环境没有交互式 shell,也没有预置的ANDROID_HOME,而且每次构建都是干净环境。这一章把commandlinetools-linux-10406996-latest.zip在容器里的落地步骤拆开,包括 Dockerfile 写法、许可预接受、以及 Gradle 如何找到 SDK。

3.1 写一个可复用的 Dockerfile 片段

下面这个 Dockerfile 片段基于一个常见的 Linux 基础镜像,把命令行工具链装进去,并预装 platform 和 build-tools。注意这里没有用latest标签的镜像,而是指定了一个稳定版本,避免基础镜像变动导致构建失败。

FROM ubuntu:22.04 # 安装基础依赖:unzip、wget、openjdk RUN apt-get update && apt-get install -y \ unzip \ wget \ openjdk-17-jdk-headless \ && rm -rf /var/lib/apt/lists/* # 设置 Android SDK 根目录 ENV ANDROID_HOME=/opt/android-sdk ENV PATH=${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH} # 创建目录并解压命令行工具 RUN mkdir -p ${ANDROID_HOME}/cmdline-tools \ && wget -q https://example.invalid/commandlinetools-linux-10406996-latest.zip -O /tmp/cmdline-tools.zip \ && unzip -q /tmp/cmdline-tools.zip -d /tmp/cmdline-tools-extract \ && mv /tmp/cmdline-tools-extract/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \ && rm -rf /tmp/cmdline-tools.zip /tmp/cmdline-tools-extract # 预接受许可并安装必要组件 RUN yes | sdkmanager --licenses \ && sdkmanager "platforms;android-34" "build-tools;34.0.0" "platform-tools"

逻辑说明:ENV设置的环境变量在后续所有RUN指令中生效,所以sdkmanager能直接找到。wget的 URL 这里用了一个占位地址,实际使用时替换成你获取 zip 包的合法来源。yes | sdkmanager --licenses在 Docker 构建阶段执行,把许可文件写到$ANDROID_HOME/licenses下,这样后续 Gradle 构建不会因为许可未接受而失败。

参数说明:openjdk-17-jdk-headless是当前 Android Gradle 插件较新版本要求的 JDK 版本,如果你项目用的是 AGP 7.x,可能需要 JDK 11。platforms;android-34和build-tools;34.0.0按项目实际需求替换。rm -rf /var/lib/apt/lists/*是为了减小镜像体积,不是必须。

3.2 许可文件的手动写入方式与 CI 缓存策略

有些 CI 环境不允许在构建阶段交互式接受许可,或者yes |在某些 shell 下行为不一致。更稳妥的做法是手动创建许可文件。sdkmanager --licenses本质上是在$ANDROID_HOME/licenses下生成几个以哈希命名的文件,内容是一串十六进制字符串。

常见做法是先在本地跑一次sdkmanager --licenses,然后把$ANDROID_HOME/licenses目录整个拷贝到 CI 的缓存或者直接提交到私有仓库。下面是一个手动写入的示例,哈希值需要和你实际接受的许可对应,不能随便编。

# 创建 licenses 目录 mkdir -p $ANDROID_HOME/licenses # 写入 android-sdk-license 的接受记录 # 注意:这里的哈希值只是示例格式,实际值必须来自你本地接受后的文件 echo "24333f8a63b6825ea9c5514f83c2829b004d1fee" > $ANDROID_HOME/licenses/android-sdk-license echo "d56f5187479451eabf01fb78af6dfcb131a6481e" >> $ANDROID_HOME/licenses/android-sdk-license

逻辑说明:android-sdk-license文件里可以有多行,每行对应一个许可哈希。sdkmanager在安装组件时会检查这个文件,如果哈希匹配就跳过询问。CI 缓存策略上,我一般会把$ANDROID_HOME下的platforms、build-tools、platform-tools和licenses四个目录做缓存,key 里带上commandlinetools-linux-10406996这个构建号,这样工具链升级时缓存自动失效。

参数说明:echo追加时用>>而不是>,避免覆盖已有内容。哈希值不能手写编造,必须从实际接受许可后的文件中提取。如果你在 CI 里用缓存,注意缓存恢复的时机要在sdkmanager安装之前,否则缓存不生效。

3.3 Gradle 如何找到 SDK:local.properties 与 ANDROID_HOME 的优先级

容器里装好 SDK 之后,Gradle 构建时还需要知道 SDK 在哪。Android Gradle 插件查找 SDK 的顺序是:项目根目录的local.properties里的sdk.dir> 环境变量ANDROID_HOME>ANDROID_SDK_ROOT。在 CI 里通常不写local.properties,直接靠ANDROID_HOME。

但如果你在本地开发时同时有多个 SDK 版本,local.properties会覆盖环境变量,导致 CI 和本地行为不一致。我一般建议在 CI 里显式检查ANDROID_HOME是否被正确传递:

# 在 CI 脚本里加一步验证 if [ -z "$ANDROID_HOME" ]; then echo "ANDROID_HOME is not set" exit 1 fi # 确认 sdkmanager 能列出已安装包 sdkmanager --list_installed # 然后执行 gradle 构建 ./gradlew assembleDebug

逻辑说明:--list_installed是sdkmanager的一个子命令,用来确认当前 SDK 根目录下已安装的组件。如果这一步输出为空或者报错,说明ANDROID_HOME指向的目录不对。./gradlew assembleDebug是常见的构建任务,执行前确保gradlew有可执行权限。

参数说明:assembleDebug是构建 debug 变体,如果你需要 release 包,换成assembleRelease,但 release 通常需要签名配置。--list_installed在较新的sdkmanager里可用,旧版本可能只支持--list。

4. 避坑与排查:commandlinetools 在 Linux 上最容易翻车的 5 个点

这一章集中讲我在实际环境里遇到过的具体问题,每个都按「现象 → 原因 → 解决」写。这些坑不限于某个发行版,Ubuntu、Debian、CentOS 上都可能出现。

4.1 现象:sdkmanager 报 “Could not determine SDK root”

原因:cmdline-tools没有放在latest目录下,或者ANDROID_HOME指向了错误的层级。sdkmanager通过自身路径向上回溯两级来推断 SDK 根目录,如果目录结构是$ANDROID_HOME/cmdline-tools/bin/sdkmanager,回溯两级得到$ANDROID_HOME,但此时cmdline-tools不在latest里,推断逻辑会混乱。

解决:严格按照$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager的结构放置。如果你已经解压错了,重新mv一次即可,不需要重新下载 zip 包。

4.2 现象:安装 platform 时提示 “Warning: Failed to read or create install properties file”

原因:$ANDROID_HOME目录权限不足,或者当前用户对$ANDROID_HOME没有写权限。在 CI 容器里如果以非 root 用户运行,而$ANDROID_HOME是 root 创建的,就会出这个问题。

解决:确保$ANDROID_HOME及其子目录对当前用户可写。Dockerfile 里可以用chown -R调整,或者在RUN之前切换用户。本地环境检查ls -ld $ANDROID_HOME。

4.3 现象:sdkmanager --licenses 在 CI 里卡住不退出

原因:sdkmanager --licenses会逐个询问,如果 CI 环境没有标准输入,或者yes命令没有正确管道,进程会挂起直到超时。

解决:用yes | sdkmanager --licenses并确保yes在 PATH 里。更稳妥的是提前写入许可文件,完全跳过交互。如果必须交互,可以用timeout 60 sdkmanager --licenses加超时保护。

4.4 现象:Gradle 构建报 “SDK location not found”

原因:ANDROID_HOME没有传递给 Gradle 进程,或者local.properties里的sdk.dir指向了不存在的路径。在 CI 里常见于环境变量只在当前 shell 生效,而 Gradle 在子进程里运行。

解决:在 CI 脚本里export ANDROID_HOME,并确认./gradlew能读到。如果项目里有local.properties,检查sdk.dir是否指向正确的绝对路径。可以在gradle.properties里加android.sdk.channel=3之类的配置,但更直接的是修环境变量。

4.5 现象:build-tools 安装成功但 aapt2 找不到

原因:build-tools目录虽然存在,但aapt2的可执行权限丢失,或者PATH里没有把build-tools的版本目录加进去。Gradle 通常会自己定位aapt2,但如果buildToolsVersion和已安装版本不匹配,就会去下载或者报错。

解决:确认$ANDROID_HOME/build-tools/34.0.0/aapt2存在且有可执行权限。如果项目指定了不同的buildToolsVersion,用sdkmanager安装对应版本。不要手动改aapt2权限,重新安装更干净。

5. 进阶技巧:用 sdkmanager 做版本锁定与离线包制作

前面几章把安装和构建链路跑通了,这一章讲两个在实际团队里很有用的技巧:一是如何锁定commandlinetools-linux-10406996-latest.zip对应的组件版本,避免latest渠道自动升级导致构建漂移;二是如何制作离线包,在内网环境里批量部署。

5.1 用 --channel 和 package.xml 锁定组件版本

sdkmanager默认从稳定渠道拉取包,但latest这个词本身暗示了渠道可能变化。如果你希望每次构建都用完全相同的 platform 和 build-tools,可以在安装时指定--channel=0(稳定渠道),并且把安装的包版本写死。

# 指定稳定渠道,避免拉到预览版 sdkmanager --channel=0 "platforms;android-34" "build-tools;34.0.0" # 查看已安装包的精确版本 sdkmanager --list_installed --verbose

逻辑说明:--channel=0对应稳定渠道,--channel=1是 beta,--channel=2是 dev,--channel=3是 canary。--verbose会输出每个包的详细版本号和安装路径。把--list_installed --verbose的输出保存下来,作为版本锁定的依据。

参数说明:--channel参数在较新的sdkmanager里支持,旧版本可能没有。如果你用的commandlinetools-linux-10406996-latest.zip里的sdkmanager不支持,可以先用--list看可用包,然后手动指定完整版本号。

5.2 制作离线 SDK 包:把 $ANDROID_HOME 打包成 tar

内网环境无法直接访问外部仓库时,可以在有网机器上装好所有组件,然后把整个$ANDROID_HOME打包,拷贝到内网解压,再设置环境变量。这个做法比逐个下载 zip 包更省事,但要注意许可文件和路径。

# 在有网机器上,装好所有需要的组件后,打包 tar -czf android-sdk-offline.tar.gz -C $HOME android-sdk # 在内网机器上解压到相同路径 tar -xzf android-sdk-offline.tar.gz -C $HOME # 设置环境变量,和之前一致 export ANDROID_HOME="$HOME/android-sdk" export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"

逻辑说明:tar -C $HOME指定打包时的基准目录,这样解压出来就是$HOME/android-sdk,路径一致。许可文件在$ANDROID_HOME/licenses下,会一起打包,内网机器不需要重新接受。如果内网机器的用户名不同,$HOME路径会变,需要相应调整ANDROID_HOME。

参数说明:-czf是创建 gzip 压缩的 tar 包,-xzf是解压。如果 SDK 目录很大,可以用-I 'zstd -T0'换用 zstd 压缩,速度更快。打包前建议先sdkmanager --list_installed确认组件齐全。

5.3 验证离线包是否可用的最小检查清单

解压完离线包之后,不要直接跑构建,先做三个检查:sdkmanager --list_installed能列出组件、adb version能输出版本号、aapt2 version能执行。这三个命令覆盖了命令行工具、平台工具和构建工具三个层面。

# 检查 sdkmanager 能否识别已安装组件 sdkmanager --list_installed # 检查 adb 是否可用 adb version # 检查 aapt2 是否可用(路径里的版本号按实际替换) $ANDROID_HOME/build-tools/34.0.0/aapt2 version

逻辑说明:sdkmanager --list_installed验证 SDK 根目录和许可文件;adb version验证platform-tools的PATH配置;aapt2 version验证build-tools的完整性。三个都通过,再跑./gradlew assembleDebug基本不会因为环境问题失败。

参数说明:aapt2的路径里的34.0.0要换成你实际安装的版本。如果aapt2 version报权限错误,用chmod +x加上可执行权限,但更推荐重新解压离线包,避免权限在打包时丢失。

我自己在团队里推这套流程时,最大的教训是:不要相信latest这个词。commandlinetools-linux-10406996-latest.zip里的10406996才是真正需要记录在构建脚本里的锚点,latest只是渠道标记。每次升级工具链,先在一个独立容器里跑通sdkmanager --list_installed和gradlew assembleDebug,再把新的构建号写进 CI 配置。这个习惯帮我省掉了至少三次因为工具链静默升级导致的构建失败。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 9:56:24

企业级Agent平台深度解析:权限、编排与审计如何驱动生产级应用

直接切入。最近一段时期,身边不少团队都在讨论同一个话题:个人用的 Agent 工具已经跑得挺顺了,怎么一搬到企业环境就各种别扭?权限管不住、流程接不上、出了问题没人能解释。正好看到某云厂商发布企业级 Agent 平台 AgentCore&…

作者头像 李华
网站建设 2026/10/22 9:55:09

Muse合规薅Claude Team免费一年:官方权益最大化实战指南

看到“用 Muse 薅 Claude Team 免费 1 年教程”这个标题,先别急着去搜破解工具,也别默认这就是什么灰色操作。我最初接触这个玩法时也以为又是什么协议漏洞,结果拆开研究才发现,Muse 根本不是专门的破解工具,它更像一个…

作者头像 李华
网站建设 2026/10/22 9:54:07

Unity同步Socket实战:从粘包原理到封包拆包完整方案

事情是这样的。我之前在做一个局域网联调小工具,偷懒直接在Unity里用了Socket同步连接去收发消息,结果连完第一天就撞上了粘包——服务器明明分了两次Send,客户端读出来却是一坨黏在一起的数据,日志完全没法看。后来我把同步Socke…

作者头像 李华
网站建设 2026/10/22 9:54:03

GitHub大文件上传限制与Git LFS配置实战指南

前几天一个同事找我,说他被一条报错卡了两个小时:推送一个包含 900MB 数据集的仓库时,GitHub 直接拒绝,提示 remote: error: File is too large 。他想让我帮他“绕过限制”,但我先问了一句:你为什么要用…

作者头像 李华
网站建设 2026/10/22 9:53:58

基于SpringBoot与Android的共享书屋图书借阅管理系统实战解析

1. 项目概述与背景洞察1.1 核心需求解析近几年社区共享阅读的概念反复被提及,但真正落地时总会遇到一些绕不过去的坎:图书散落在不同楼层、不同社区,靠纸笔登记效率低、易出错;借阅记录无法实时追踪,图书丢失、逾期归还…

作者头像 李华