Androidアプリのビルド環境Dockerイメージ制作

# Androidアプリのビルド環境Dockerイメージ制作

gitlab-ciで、Androidアプリのビルドを行う際に、openjdkのイメージを元に、毎回環境を作っていてパイプラインの時間が長いので、ビルド環境Dockerイメージを制作した際にあったいろいろな事を記録しておきます。

# ビルド環境として必要なもの

Androidアプリのビルドを行うために必要なものは、基本的には以下のものが必要となります。

- Java Development Kit(以下、JDK)
    - Javaの開発キットです。コンパイラなど開発に必要なツールを含みます。
    - openjdkを使います。
- [Android command line tools](https://developer.android.com/studio)（以下、CLT)
    - AndroidSDKや必要となるパッケージをインストールするsdkmanagerが含まれます。
    - sdk-toolsとcommandlinetoolsがあります。以前は、sdk-toolsでしたが、現在commandlinetoolsしか公式ページからはダウンロードできません。
    - 最低限必要となるパッケージには以下のものがあります。
        - build-tools
        - platforms
        - platform-tools

- [gradle](https://gradle.org/)
    - Java（JVM）環境におけるビルドシステムのことで、オープンソースプロジェクトとして無償で公開されています。
特長としては、ビルドの記述を XML のような構造ではなく、Groovy ライクな 独自の DSL を用い、スクリプトとして記述できる点です。用意されているAPIを利用することで、強力な記述が可能となります。

`これ以外にも、ndk（ndk-bundle）や、その他パッケージが必要となるアプリもあると思います。`

# どんなイメージにするか？

元々、gitlab-ciで行っていた環境構築をDokerfileにしていくのですが、build-toolsやplatformsなどの必要となるパッケージまでをインストールした状態のイメージはサイズが大きくなってしまいます。ndkなど入れてしまったら、5G超えしてしまいそう・・・と言う事で、２種類用意することにしました。

用途1） JDK+CLT+gradleまでインストールしたイメージ
用途2） JDK+CLT+gradle+必要なパッケージまでインストールしたイメージ

`【使い分け】`
用途1）の場合は、必要なパッケージのインストールはgradleやgradlewにお任せします。毎回、パッケージのインストールを行う事にはなりますが、パッケージのバージョンは可変にできます。

用途2）の場合は、必要なパッケージのバージョンは固定ですが、パッケージのインストール時間を削減できます。

# gitlab-ciで行っていた環境構築をDokerfileにしていく

## sdk-toolsの用途1）の場合

sdk-toolsの用途1）の場合のDockerfileが以下となります。
sdk-toolsのlicensesに同意するまでで終わります。

```Dockerfile
# JDKが必要なので、ベースイメージにopenjdkを使います。
FROM openjdk:8-jdk-slim

# args default
# CLTとgradleのバージョンのデフォルト値
ARG ANDROID_SDK_TOOLS="4333796"
ARG GRADLE_VERSION="5.6.4"

# apt-utilsをインストールしなくても警告が表示されいようにする
ENV DEBCONF_NOWARNINGS yes

# sdkmanager を使って現在のマシンとは異なるオペレーティングシステム用のパッケージをダウンロードする場合に使います。
# 今回は、openjdk:8-jdk-slimがAlpine Linuxなので指定しなくても大丈夫なのですが、明示的に指定しておきます。
ENV REPO_OS_OVERRIDE "linux"

# 途中の作業で必要になるパッケージをインストールします。
# 少しでも軽くするために不要なパッケージを自動削除します。
# 本当なら最後に、wgetやunzipなども削除すべきです。
RUN apt-get update --quiet --yes \
    && apt-get autoremove --quiet --yes \
    && apt-get install --quiet --yes wget \
    && apt-get install --quiet --yes unzip \
    && rm -rf /var/lib/apt/lists/*

# CLTをダウンローして解凍します。
# sdk-toolsなら、sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
# commandlinetoolsなら、commandlinetools-linux-${ANDROID_SDK_TOOLS}_latest.zip
# とします。
RUN wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
RUN unzip -q -d android-sdk-linux android-sdk.zip
RUN rm -rf android-sdk.zip

# gradleをダウンローして解凍します。
RUN wget --quiet --output-document=gradle-bin.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip
RUN unzip -q -d gradle gradle-bin.zip
RUN rm -rf gradle-bin.zip

# 環境変数
ENV ANDROID_HOME $PWD/android-sdk-linux
ENV ANDROID_SDK_ROOT $ANDROID_HOME/tools
ENV GRADLE_HOME $PWD/gradle/gradle-${GRADLE_VERSION}
ENV PATH $PATH:$ANDROID_SDK_ROOT/bin:$GRADLE_HOME/bin

# sdk-toolsのlicensesに同意します。
RUN mkdir ~/.android && \
    touch ~/.android/repositories.cfg
RUN yes | sdkmanager --licenses >/dev/null
```

## 用途2）の場合

用途2）の場合は、sdk-toolsのlicensesに同意した後に続けて、必要なパッケージのインストールを行います。

```Dockerfile
# sdk-toolsのlicensesに同意します。
RUN mkdir ~/.android && \
    touch ~/.android/repositories.cfg
RUN yes | sdkmanager --licenses >/dev/null

# 必要なパッケージのインストール
RUN sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
RUN ANDROID_COMPILE_SDK="${ANDROID_BUILD_TOOLS%%.*}"; sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
RUN sdkmanager "platform-tools" >/dev/null
```

## sdk-toolsのlicensesに同意する

sdk-toolsのlicensesに同意すると、$ANDROID_HOME/licensesと言うディレクトリが作成されその中に、いくつかファイルが作成されます。

sdkmanager --licensesで、ライセンスへの同意を一括で行えます。

これを行わないと、パッケージのインストールで毎回[y/N]を聞かれる事になります。

```Dockerfile
# sdk-toolsのlicensesに同意します。
RUN mkdir ~/.android && \
    touch ~/.android/repositories.cfg
RUN yes | sdkmanager --licenses >/dev/null
```

## commandlinetoolsの場合

commandlinetoolsをダウンロードして使用する場合は、sdkmanagerにオプション「--sdk_root」が必要でした。

```Dockerfile
# sdk-toolsのlicensesに同意します。
RUN mkdir ~/.android && \
    touch ~/.android/repositories.cfg
RUN yes | sdkmanager --sdk_root=${ANDROID_HOME} --licenses >/dev/null
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~

# 必要なパッケージのインストール
RUN sdkmanager --sdk_root=${ANDROID_HOME} "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
RUN ANDROID_COMPILE_SDK="${ANDROID_BUILD_TOOLS%%.*}"; \
    sdkmanager --sdk_root=${ANDROID_HOME} "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
RUN sdkmanager --sdk_root=${ANDROID_HOME} "platform-tools" >/dev/null
```

# 作成したDokerイメージを実際に使ってみる

## 用途1

必要なパッケージのインストールはgradleがやってくれているのが分かります。

```
$ docker-compose up
Creating android_build_sdk-tools ... done
Attaching to android_build_sdk-tools
android_build_sdk-tools | 
android_build_sdk-tools | Welcome to Gradle 5.6.4!
android_build_sdk-tools | 
android_build_sdk-tools | Here are the highlights of this release:
android_build_sdk-tools |  - Incremental Groovy compilation
android_build_sdk-tools |  - Groovy compile avoidance
android_build_sdk-tools |  - Test fixtures for Java projects
android_build_sdk-tools |  - Manage plugin versions via settings script
android_build_sdk-tools | 
android_build_sdk-tools | For more details see https://docs.gradle.org/5.6.4/release-notes.html
android_build_sdk-tools | 
android_build_sdk-tools | Starting a Gradle Daemon (subsequent builds will be faster)
android_build_sdk-tools | 
android_build_sdk-tools | > Configure project :app
android_build_sdk-tools | Checking the license for package Android SDK Build-Tools 28.0.3 in /android-sdk-linux/licenses
android_build_sdk-tools | License for package Android SDK Build-Tools 28.0.3 accepted.
android_build_sdk-tools | Preparing "Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)".
android_build_sdk-tools | "Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)" ready.
android_build_sdk-tools | Installing Android SDK Build-Tools 28.0.3 in /android-sdk-linux/build-tools/28.0.3
android_build_sdk-tools | "Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)" complete.
android_build_sdk-tools | "Install Android SDK Build-Tools 28.0.3 (revision: 28.0.3)" finished.
android_build_sdk-tools | Checking the license for package Android SDK Platform 28 in /android-sdk-linux/licenses
android_build_sdk-tools | License for package Android SDK Platform 28 accepted.
android_build_sdk-tools | Preparing "Install Android SDK Platform 28 (revision: 6)".
android_build_sdk-tools | "Install Android SDK Platform 28 (revision: 6)" ready.
android_build_sdk-tools | Installing Android SDK Platform 28 in /android-sdk-linux/platforms/android-28
android_build_sdk-tools | "Install Android SDK Platform 28 (revision: 6)" complete.
android_build_sdk-tools | "Install Android SDK Platform 28 (revision: 6)" finished.
android_build_sdk-tools | Checking the license for package Android SDK Platform-Tools in /android-sdk-linux/licenses
android_build_sdk-tools | License for package Android SDK Platform-Tools accepted.
android_build_sdk-tools | Preparing "Install Android SDK Platform-Tools (revision: 29.0.6)".
android_build_sdk-tools | "Install Android SDK Platform-Tools (revision: 29.0.6)" ready.
android_build_sdk-tools | Installing Android SDK Platform-Tools in /android-sdk-linux/platform-tools
android_build_sdk-tools | "Install Android SDK Platform-Tools (revision: 29.0.6)" complete.
android_build_sdk-tools | "Install Android SDK Platform-Tools (revision: 29.0.6)" finished.
android_build_sdk-tools | 
android_build_sdk-tools | > Task :app:preBuild UP-TO-DATE
```

## 用途2

必要なパッケージのインストールは既に行われているイメージを使うので
gradleはインストールを省略しているのが分かります。

```
$ docker-compose up
Creating android_build_sdk-tools-28.0.3 ... done
Attaching to android_build_sdk-tools-28.0.3
android_build_sdk-tools-28.0.3 | 
android_build_sdk-tools-28.0.3 | Welcome to Gradle 5.6.4!
android_build_sdk-tools-28.0.3 | 
android_build_sdk-tools-28.0.3 | Here are the highlights of this release:
android_build_sdk-tools-28.0.3 |  - Incremental Groovy compilation
android_build_sdk-tools-28.0.3 |  - Groovy compile avoidance
android_build_sdk-tools-28.0.3 |  - Test fixtures for Java projects
android_build_sdk-tools-28.0.3 |  - Manage plugin versions via settings script
android_build_sdk-tools-28.0.3 | 
android_build_sdk-tools-28.0.3 | For more details see https://docs.gradle.org/5.6.4/release-notes.html
android_build_sdk-tools-28.0.3 | 
android_build_sdk-tools-28.0.3 | Starting a Gradle Daemon (subsequent builds will be faster)
android_build_sdk-tools-28.0.3 | 
android_build_sdk-tools-28.0.3 | > Configure project :app
android_build_sdk-tools-28.0.3 | > Task :app:preBuild UP-TO-DATE
```

# 最後に

あとは、DockerHubに公開しておけばOKですね！

commandlinetoolsの登場で、sdkmanagerのオプションが必要になった事に気が付くまで結構ハマってしまいました。

レイヤーなどを上手く使えるともう少し良いのだろうとは思いますが、またの機会に、、、

# 参考にさせて頂きましたありがとうございました！

[DockerでAndroidアプリのビルド環境を作る](https://qiita.com/kichinaga/items/66872432747e76d72af7)