When developing a project for the Android platform, even the smallest project will sooner or later require a development environment. In addition to the Android SDK, you need to have the latest versions of Kotlin, Gradle, platform-tools, and build-tools. While these dependencies can be managed through Android Studio IDE on a developer's machine, every update on a CI/CD server can become a headache. In web development, Docker has become the standard solution for environment issues, so why not try to solve a similar problem in Android development with it...
For those who don’t know what Docker is — to put it simply, it’s a tool for creating so-called “containers” that contain a minimal OS core and the necessary software stack, which we can deploy wherever we want while preserving the environment. What will be in our container is defined in the Dockerfile, which is then built into an image that can be run anywhere and possesses properties of idempotency.
The installation process and basics of Docker are well described on its . So, looking ahead a bit, here's the Dockerfile we've created
# Т.к. основным инструментом для сборки Android-проектов является Gradle,
# и по счастливому стечению обстоятельств есть официальный Docker-образ
# мы решили за основу взять именно его с нужной нам версией Gradle
FROM gradle:5.4.1-jdk8
# Задаем переменные с локальной папкой для Android SDK и
# версиями платформы и инструментария
ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip"
ANDROID_HOME="/usr/local/android-sdk"
ANDROID_VERSION=28
ANDROID_BUILD_TOOLS_VERSION=28.0.3
# Создаем папку, скачиваем туда SDK и распаковываем архив,
# который после сборки удаляем
RUN mkdir "$ANDROID_HOME" .android
&& cd "$ANDROID_HOME"
&& curl -o sdk.zip $SDK_URL
&& unzip sdk.zip
&& rm sdk.zip
# В следующих строчках мы создаем папку и текстовые файлы
# с лицензиями. На оф. сайте Android написано что мы
# можем копировать эти файлы с машин где вручную эти
# лицензии подтвердили и что автоматически
# их сгенерировать нельзя
&& mkdir "$ANDROID_HOME/licenses" || true
&& echo "24333f8a63b6825ea9c5514f83c2829b004d1" > "$ANDROID_HOME/licenses/android-sdk-license"
&& echo "84831b9409646a918e30573bab4c9c91346d8" > "$ANDROID_HOME/licenses/android-sdk-preview-license"
# Запускаем обновление SDK и установку build-tools, platform-tools
RUN $ANDROID_HOME/tools/bin/sdkmanager --update
RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}"
"platforms;android-${ANDROID_VERSION}"
"platform-tools"
We save it in the folder with our Android project and start the container build with the command
docker build -t android-build:5.4-28-27 .Parameter -t sets a tag or name for our container, which typically consists of its name and version. In our case, we've called it android-build and specified a version that reflects the combined versions of gradle, android-sdk, and platform-tools. This makes it easier to search for the desired image by name using this 'version'.
Once the build is complete, we can use our image locally; we can upload it with the command docker push to a public or private image repository to download it on other machines.
As an example, let’s build the project locally. For this, in the project folder, we execute the command
docker run --rm -v "$PWD":/home/gradle/ -w /home/gradle android-build:5.4.1-28-27 gradle assembleDebugLet's break down what it means:
docker run — the command to start the image.
-rm — means that after stopping the container, it will remove everything that was created during its lifetime.
-v "$PWD":/home/gradle/ — mounts the current folder with our Android project to the internal folder of the container /home/gradle/
-w /home/gradle — sets the working directory of the container.
android-build:5.4.1-28-27 — the name of our container that we built
gradle assembleDebug — the actual build command that compiles our project
If everything goes well, in a couple of seconds/minutes you will see something like this on your screen BUILD SUCCESSFUL in 8m 3sAnd in the folder app/build/output/apk, the compiled application will be stored.
Similarly, you can perform other gradle tasks — checking the project, running tests, etc. The main advantage is that when you need to build the project on another machine, you don't have to worry about setting up the entire environment; you just need to download the necessary image and run the build in it.
The container does not store any changes, and each build starts from scratch. This guarantees build consistency regardless of where it is run, but on the downside, every time you have to download all dependencies and compile the entire code again, which can sometimes take significant time. Therefore, in addition to the usual 'cold' start, we have an option to start the build with caching, where we save the folder ~/ .gradle by simply copying it to the project's working folder, and at the beginning of the next build, we return it back. We separated all copying procedures into individual scripts, and our start command now looks like this
docker run --rm -v "$PWD":/home/gradle/ -w /home/gradle android-build:5.4.1-28-27 /bin/bash -c "./pre.sh; gradle assembleDebug; ./post.sh"As a result, the average build time of our project has been reduced several times (depending on the number of dependencies in the project, but an average project now builds in 1 minute instead of 5 minutes).
All of this makes sense only if you have your own internal CI/CD server, which you manage yourself. But now there are many cloud services where all these issues are resolved and you don’t have to worry about it, and the necessary build properties can also be specified in the project settings.
Only registered users can participate in the survey. , please.
Do you keep the CI/CD system in-house or use a third-party service?
We use an internal server
We use an external service
We do not use CI/CD
Other
42 users voted. 16 users abstained.
Source: habr.com
