Automate Android App Builds with Jenkins and Docker: A Step‑by‑Step Guide
Learn how to set up a complete Android CI/CD pipeline by installing JDK, Android SDK, and Gradle, creating Docker images for builds, configuring Jenkins on Kubernetes, and automating packaging and deployment, with detailed commands and troubleshooting tips for efficient mobile app releases.
Introduction
As automation becomes widespread, many companies automate application releases. While front‑end and back‑end projects are often automated, mobile projects are not. This guide outlines steps to automate Android builds, starting with manual testing and moving toward full automation.
Environment Setup
Install JDK
Download JDK from Oracle and extract it, then set JAVA_HOME and update PATH.
<code># tar xf jdk-8u291-linux-x64.tar.gz -C /usr/local/
# vim /etc/profile
export JAVA_HOME=/usr/local/jdk1.8.0_291
export PATH=$PATH:$JAVA_HOME/bin
# source /etc/profile
# java -version
java version "1.8.0_291"
...</code>Install Android SDK
Download the command‑line tools from the Android developer site and configure ANDROID_HOME.
<code># unzip commandlinetools-linux-7302050_latest.zip -d /usr/local/
# vim /etc/profile
export ANDROID_HOME=/usr/local
export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/cmdline-tools/bin
# source /etc/profile
# sdkmanager --list
# sdkmanager "platforms;android-29"
</code>If you encounter “Could not determine SDK root” errors, create a
latestdirectory under
cmdline-toolsand move
bin,
lib, and
source.propertiesinto it.
<code>latest/
├── bin
├── lib
├── NOTICE.txt
└── source.properties
</code>Then update the environment variables accordingly.
<code># vim /etc/profile
export ANDROID_HOME=/usr/local
export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/cmdline-tools/latest/bin
</code>Install Gradle
Download Gradle and unpack it, then set GRADLE_HOME and update PATH.
<code># unzip gradle-7.1.1-bin.zip -d /usr/local/
# vim /etc/profile
export GRADLE_HOME=/usr/local/gradle-7.1.1
export PATH=$PATH:$JAVA_HOME/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$GRADLE_HOME/bin
# source /etc/profile
# gradle -v
Welcome to Gradle 7.1.1!
...
</code>Manual Build Test
Clone Repository
<code># git clone http://192.168.100.180/app/android/newcrm1.0.git
</code>Build and Package
<code># gradle build & gradle assemble
# curl -F "file=@/tmp/example.ipa" -F "uKey=aa18132c4d9afedfa9cd2c054213c867" -F "_api_key=bb66fdd1c5a4c247b016e0ab88a54fdd" https://upload.pgyer.com/apiv1/app/upload
</code>Automated Build with Jenkins
Jenkins runs in Kubernetes; Android builds can be executed on a Jenkins‑slave container.
Workflow diagram:
Create Docker Image for Build
Build a Docker image that contains the Android SDK and Gradle.
<code>FROM gradle:5.6.4-jdk8
ADD cmdline-tools.tar.gz /usr/local
ENV ANDROID_HOME /usr/local
ENV PATH ${ANDROID_HOME}/cmdline-tools/latest/bin:${PATH}
</code>Build the image:
<code>docker build -t registry.cn-hangzhou.aliyuncs.com/rookieops/android-sdk:v1 .
</code>Install additional SDK components inside a running container, then commit the container as a new image.
<code>docker exec -it --rm registry.cn-hangzhou.aliyuncs.com/rookieops/android-sdk:v1 bash
# sdkmanager "platforms;android-29" "platform-tools" "build-tools;29.0.2"
# docker commit <container_id> registry.cn-hangzhou.aliyuncs.com/rookieops/android-sdk:v2
# docker push registry.cn-hangzhou.aliyuncs.com/rookieops/android-sdk:v2
</code>Jenkinsfile
The Jenkinsfile defines the pipeline, pulls the custom image, builds the APK, and uploads it.
<code>#!groovy
@Library('lotbrick') _
def dingmes = new org.devops.sendDingTalk()
pipeline {
agent {
kubernetes {
label "jenkins-slave-${UUID.randomUUID().toString()}"
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: gradle
image: registry.cn-hangzhou.aliyuncs.com/rookieops/android-sdk:v2
command: ['cat']
tty: true
volumeMounts:
- name: caches
mountPath: /home/gradle/.gradle/caches/
volumes:
- name: caches
hostPath:
path: "/data/jenkins-job/${JOB_NAME}/gradle/"
"""
}
}
environment {
GIT_CREDENTIAL_ID = 'git-token'
UKEY = "xxxx"
API_KEY = "xxx"
UPLOAD_URL = "https://upload.pgyer.com/apiv1/app/upload"
DINGTALKHOOK = "https://oapi.dingtalk.com/robot/send?access_token=..."
}
triggers {
GenericTrigger(
genericVariables: [
[key: 'ref', value: '$.ref'],
[key: 'before', value: '$.before'],
[key: 'after', value: '$.after'],
[key: 'hook_name', value: '$.hook_name']
],
causeString: 'Triggered on $ref',
token: env.JOB_NAME,
printContributedVariables: true,
printPostContent: true,
regexpFilterText: '$ref',
regexpFilterExpression: 'refs/heads/(pre|master)'
)
}
options {
timeout(time: 25, unit: 'MINUTES')
}
stages {
stage('Checkout SCM') {
steps {
checkout(scm)
}
}
stage('Build & Push') {
steps {
container('gradle') {
script {
sh """
gradle build & gradle assemble
ls app/build/outputs/apk/release/*.apk
"""
PACKAGE_DIR = sh(script: "ls app/build/outputs/apk/release/*.apk", returnStdout: true).trim()
FULL_DIR = "${WORKSPACE}/${PACKAGE_DIR}"
sh """
curl -F \"file=@${FULL_DIR}\" -F \"uKey=${UKEY}\" -F \"_api_key=${API_KEY}\" ${UPLOAD_URL}
"""
}
}
}
}
}
post {
success {
script {
println "success: only runs on build success"
dingmes.SendDingTalk("Build succeeded ✅")
}
}
failure {
script {
println "failure: only runs on build failure"
dingmes.SendDingTalk("Build failed ❌")
}
}
}
}
</code>Configure Pipeline in Kubesphere
Add the project, set the repository branch and path, and save the configuration. After a successful build, DingTalk receives a notification and the DevOps console shows detailed build information.
Conclusion
The guide demonstrates how to use Jenkins to publish an Android project. The process is straightforward, though the Docker image that includes the Android SDK can be large (around 1 GB), making it the biggest image built so far.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.