从“装环境装到怀疑人生”到“一条命令打出APK”

前言

在上篇《Docker从零入门》中,我们学习了Docker的基本概念和常用命令,并成功运行了Nginx容器。今天我们进入实战环节——用Docker封装安卓打包环境

安卓开发的小伙伴都知道,搭建一个完整的安卓编译环境有多“坑”:

  • 需要安装特定版本的JDK
  • 下载数十GB的Android SDK
  • 配置各种环境变量
  • 不同项目可能需要不同版本的SDK和Gradle
  • 团队协作时,每个人环境不一致,导致“在我电脑上能打包,你的就不行”

用Docker解决这些痛点:把整个编译环境打包进镜像,任何人只要拉取镜像,就能在完全一致的环境下打出APK。

本文将以一个简单的安卓项目为例,手把手教你构建一个可复用的安卓打包镜像,并实现一键打包。


1. 准备工作

在开始前,确保你已经安装了Docker(参考上篇),并准备一个安卓项目(例如一个简单的HelloWorld工程,包含build.gradle等文件)。

我们将使用以下技术栈:

  • 基础镜像openjdk:8-jdk(安卓编译需要JDK 8)
  • SDK管理工具sdkmanager(Android SDK命令行工具)
  • 构建工具:Gradle(使用项目内的Gradle Wrapper)

2. 编写Dockerfile

我们新建一个目录,比如android-builder,在里面创建Dockerfile和后续会用到的脚本。

2.1 选择基础镜像并安装必要工具

# 使用官方OpenJDK 8镜像作为基础
FROM openjdk:8-jdk

# 设置环境变量,避免安装时交互
ENV DEBIAN_FRONTEND=noninteractive

# 安装必要工具
RUN apt-get update && apt-get install -y \
    wget \
    unzip \
    git \
    && rm -rf /var/lib/apt/lists/*

2.2 下载并配置Android SDK命令行工具

安卓官方提供了commandlinetools,用于安装SDK组件。

# 设置Android SDK根目录
ENV ANDROID_HOME=/opt/android-sdk
ENV PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools

# 创建工作目录并下载命令行工具
RUN mkdir -p $ANDROID_HOME/cmdline-tools \
    && cd $ANDROID_HOME/cmdline-tools \
    && wget -q https://dl.google.com/android/repository/commandlinetools-linux-latest.zip \
    && unzip commandlinetools-linux-latest.zip \
    && mv cmdline-tools latest \
    && rm commandlinetools-linux-latest.zip

# 接受许可并安装必要的SDK组件(这里仅安装构建所需的最小集)
RUN yes | sdkmanager --licenses \
    && sdkmanager "platform-tools" "platforms;android-30" "build-tools;30.0.3"

说明

  • 我们指定了android-30(对应Android 11)和build-tools 30.0.3,你可以根据项目需求修改。
  • yes命令自动接受所有许可证。

2.3 设置工作目录

WORKDIR /app

3. 完整Dockerfile

将以上内容整合,最终的Dockerfile如下:

FROM openjdk:8-jdk

ENV DEBIAN_FRONTEND=noninteractive
ENV ANDROID_HOME=/opt/android-sdk
ENV PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools

RUN apt-get update && apt-get install -y \
    wget \
    unzip \
    git \
    && rm -rf /var/lib/apt/lists/*

RUN mkdir -p $ANDROID_HOME/cmdline-tools \
    && cd $ANDROID_HOME/cmdline-tools \
    && wget -q https://dl.google.com/android/repository/commandlinetools-linux-latest.zip \
    && unzip commandlinetools-linux-latest.zip \
    && mv cmdline-tools latest \
    && rm commandlinetools-linux-latest.zip

RUN yes | sdkmanager --licenses \
    && sdkmanager "platform-tools" "platforms;android-30" "build-tools;30.0.3"

WORKDIR /app

4. 构建镜像

Dockerfile所在目录执行:

docker build -t android-builder .

这个过程会下载JDK、SDK命令行工具和指定的平台组件,耗时取决于网速(SDK组件较大)。构建完成后,我们就有了一枚“安卓打包镜像”。

配图建议:终端中docker build过程截图,展示正在下载SDK组件。

5. 使用容器打包安卓项目

5.1 挂载项目源码

假设你的安卓项目位于宿主机的/path/to/your-android-project,我们通过-v将项目目录挂载到容器的/app

5.2 执行Gradle构建

进入项目目录,运行:

docker run --rm \
  -v $(pwd):/app \
  android-builder \
  ./gradlew assembleDebug

命令解析:

  • --rm:容器退出后自动删除,避免残留。
  • -v $(pwd):/app:将当前目录(项目根目录)挂载到容器的/app,容器内的操作会直接反映到宿主机。
  • android-builder:我们构建的镜像名。
  • ./gradlew assembleDebug:容器内执行的命令,使用项目自带的Gradle Wrapper打Debug包。

配图建议:终端中执行该命令,显示Gradle开始构建的日志。

5.3 获取APK

构建成功后,APK会生成在项目的app/build/outputs/apk/debug/目录下,直接在宿主机就能看到。

如果你希望APK输出到指定目录,可以再挂载一个输出目录,但上述方式已足够。

6. 优化与进阶

6.1 利用构建缓存加速

每次打包都重新下载依赖很慢,我们可以将Gradle缓存挂载出来,避免重复下载。

创建一个本地目录用于缓存:

mkdir -p ~/.gradle-cache

然后在运行命令时加上:

docker run --rm \
  -v $(pwd):/app \
  -v ~/.gradle-cache:/root/.gradle \
  android-builder \
  ./gradlew assembleDebug

这样Gradle的依赖缓存会保留在宿主机,下次构建快得多。

6.2 封装成脚本

为了简化操作,可以写一个脚本build.sh

#!/bin/bash
docker run --rm \
  -v $(pwd):/app \
  -v ~/.gradle-cache:/root/.gradle \
  android-builder \
  ./gradlew $@

赋予执行权限后,就可以直接运行./build.sh assembleDebug了,甚至支持传递其他Gradle任务。

6.3 多版本SDK支持

如果你的项目需要多个SDK版本,可以在Dockerfile中通过参数化构建,或者构建多个标签。例如:

ARG SDK_VERSION=30
ENV SDK_VERSION=$SDK_VERSION
RUN sdkmanager "platforms;android-$SDK_VERSION"

构建时指定:

docker build --build-arg SDK_VERSION=29 -t android-builder-29 .

7. 常见问题及解决

Q1:构建时提示“SDK license not accepted”
A:确保Dockerfile中已执行yes | sdkmanager --licenses,若仍提示,可尝试在容器内手动接受。

Q2:Gradle构建慢,下载依赖超时
A:挂载Gradle缓存(如上6.1),或使用国内镜像源(在项目的build.gradle中配置阿里云镜像)。

Q3:容器内找不到./gradlew
A:确保你的项目已经包含Gradle Wrapper文件(gradlewgradle/wrapper目录)。如果没有,可以在容器内生成,但建议项目自带。

Q4:打包失败,提示找不到android-30
A:检查Dockerfile中安装的平台版本与项目build.gradle中的compileSdkVersion是否一致,或通过build.gradle指定SDK版本。

8. 总结

通过Docker,我们成功将复杂的安卓打包环境封装成一个镜像,实现了:

  • 环境一致性:所有人使用同一个镜像,保证构建结果可重现。
  • 简化部署:无需在CI服务器上手动安装SDK,只需拉取镜像即可。
  • 快速切换:不同项目可以使用不同标签的镜像,互不干扰。

本文首发于个人博客,欢迎分享。

更多推荐