24、构建环境管理:JDK版本管理、Gradle版本管理、多版本SDK管理、Docker构建环境

说实话,构建环境管理这件事,很多团队都吃过亏。

我记得刚带项目那会儿,有个同事本地编译好好的,一提交CI就挂。查了半天,原来是JDK版本不一样。他用的JDK 11,CI上配的是JDK 8。这种问题,说白了就是环境没管好。

今天咱们就把这块彻底理清楚。从JDK版本管理,到Gradle版本切换,再到多版本SDK共存,最后聊聊Docker怎么一劳永逸解决环境问题。

24.1 JDK版本管理:jenv 和 sdkman

Android开发离不开JDK。但不同项目对JDK版本要求不一样。老项目可能要用JDK 8,新项目已经切到JDK 17了。你总不能每次手动改环境变量吧?

我个人的习惯是用 sdkman 来管理JDK。当然,jenv也是个好选择。咱们分别看看。

24.1.1 用 sdkman 管理 JDK

sdkman 是Java生态里最流行的版本管理工具。安装很简单:

curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

装好之后,看看有哪些JDK版本可用:

sdk list java

输出会列出所有发行版,比如 11.0.12-open17.0.1-tem 等等。安装指定版本:

sdk install java 17.0.1-tem

切换版本也简单:

sdk use java 11.0.12-open

如果你想设置默认版本:

sdk default java 17.0.1-tem
我的小技巧: 每个项目根目录放一个 .sdkmanrc 文件,里面写上 java=11.0.12-open。这样进入目录时,sdkman 会自动切换版本。省心。

24.1.2 用 jenv 管理 JDK

jenv 是另一个选择。如果你习惯用 pyenvrbenv,那 jenv 的用法你肯定熟悉。

brew install jenv  # macOS
# 或者从源码安装

添加JDK路径:

jenv add /Library/Java/JavaVirtualMachines/jdk-11.0.12.jdk/Contents/Home

设置全局版本:

jenv global 11.0

设置局部版本(项目级别):

jenv local 17.0
注意: jenv 只是管理 JAVA_HOME 环境变量。它不会帮你下载JDK。你得自己先装好JDK,再用 jenv 去注册。而 sdkman 是下载+管理一条龙。我个人更推荐 sdkman。

24.2 Gradle版本管理:sdkman 搞定

Gradle版本切换,sdkman 同样能搞定。而且比手动下载zip包方便太多了。

查看可用版本:

sdk list gradle

安装指定版本:

sdk install gradle 7.5.1

切换版本:

sdk use gradle 8.0.2

设置默认:

sdk default gradle 7.5.1

我曾经遇到过一个坑:项目用 Gradle 7.4,但CI上装的是7.6。结果某些API行为变了,编译报错。后来我在项目里加了 gradle-wrapper.properties 指定版本,本地也用 sdkman 切到对应版本,问题才解决。

核心原则: 项目里用 gradle-wrapper.properties 锁定Gradle版本。本地开发用 sdkman 保持与项目一致。CI环境也一样。三处统一,就不会出幺蛾子。

24.3 多版本SDK管理

Android SDK 版本管理,主要靠 sdkmanager 命令行工具。它随 Android Studio 一起安装,在 $ANDROID_HOME/cmdline-tools/latest/bin/ 下。

列出已安装的SDK:

sdkmanager --list

安装指定版本的SDK平台:

sdkmanager "platforms;android-33" "platforms;android-32"

安装构建工具:

sdkmanager "build-tools;33.0.0" "build-tools;32.0.0"

安装NDK:

sdkmanager "ndk;25.2.9519653"

多版本共存时,项目通过 compileSdkVersionbuildToolsVersion 来指定用哪个。你本地装多个版本完全没问题。

我的做法: 我会在 ~/.bashrc~/.zshrc 里设置 ANDROID_HOMEANDROID_SDK_ROOT。然后 sdkmanager 的路径加到 PATH 里。这样不管哪个项目,都能找到SDK。

24.4 Docker构建环境:一劳永逸的方案

前面说的这些,都是本地管理。但团队协作时,每个人系统不一样,环境配置总有差异。这时候,Docker 就是终极方案。

用 Docker 打包一个标准的 Android 构建环境,包含指定版本的 JDK、Gradle、SDK。所有人用同一个镜像,构建结果完全一致。

下面是一个 Dockerfile 示例:

FROM ubuntu:22.04

# 安装基础工具
RUN apt-get update && apt-get install -y \
    curl \
    unzip \
    git \
    openjdk-17-jdk \
    && rm -rf /var/lib/apt/lists/*

# 设置 JAVA_HOME
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH=$JAVA_HOME/bin:$PATH

# 安装 Android SDK
ENV ANDROID_HOME=/opt/android-sdk
ENV ANDROID_SDK_ROOT=$ANDROID_HOME

RUN mkdir -p $ANDROID_HOME/cmdline-tools && \
    curl -o cmdline-tools.zip \
    https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip && \
    unzip cmdline-tools.zip -d $ANDROID_HOME/cmdline-tools && \
    mv $ANDROID_HOME/cmdline-tools/cmdline-tools $ANDROID_HOME/cmdline-tools/latest && \
    rm cmdline-tools.zip

ENV PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$PATH

# 接受许可并安装SDK
RUN yes | sdkmanager --licenses && \
    sdkmanager "platforms;android-33" "build-tools;33.0.0" "ndk;25.2.9519653"

# 设置工作目录
WORKDIR /workspace

构建镜像:

docker build -t android-build:1.0 .

运行构建:

docker run --rm -v $(pwd):/workspace android-build:1.0 ./gradlew assembleRelease
为什么推荐Docker? 因为环境一致性。本地、CI、同事电脑,都用同一个镜像。JDK版本、Gradle版本、SDK版本、NDK版本,全部锁定。不会出现「我本地能编译,你那边不行」的情况。

我曾经在一个大项目里,团队20多人,每个人环境都不一样。有人用macOS,有人用Windows,有人用Linux。每次出问题,排查环境就要花半天。后来我推了Docker方案,把构建环境做成镜像。从那以后,环境相关的问题几乎绝迹。

24.5 知识体系总览

下面这张图,把本章的核心逻辑串起来了:

构建环境管理知识体系 构建环境管理 JDK版本管理 sdkman / jenv 多版本切换 / .sdkmanrc Gradle版本管理 sdkman 安装/切换 gradle-wrapper 锁定 多版本SDK管理 sdkmanager 命令行 platforms / build-tools / NDK Docker构建环境 Dockerfile → 镜像 → 一致性构建

24.6 避坑指南

最后,分享几个我踩过的坑:

  • JDK版本不匹配: 我曾经用JDK 17编译一个老项目,结果Gradle插件不兼容,报了一堆错。后来切回JDK 11,一切正常。所以,项目文档里一定要写明JDK版本。
  • Gradle Wrapper 没提交: 有同事把 gradle-wrapper.properties 加到 .gitignore 里了。结果每个人本地Gradle版本都不一样,构建行为千奇百怪。记住,这个文件必须提交到版本库。
  • SDK路径含空格: Windows上默认SDK路径在 C:\Program Files\ 下,有空格。某些老版本Gradle会解析失败。我建议把SDK装到无空格的路径,比如 C:\Android\Sdk
  • Docker镜像不清理: 频繁构建Docker镜像,磁盘会爆。记得定期清理 docker system prune

嗯,环境管理这块,说白了就是「统一」二字。工具统一、版本统一、流程统一。做到这三点,你就能省下大量排查环境问题的时间,专心写代码。


公众号:蓝海资料掘金营,微信deep3321