Gradle ビルドの概要

Android アプリは通常、Gradle ビルドシステムを使用してビルドされます。ビルドの構成方法の詳細に入る前に、システム全体を理解できるように、ビルドの背後にあるコンセプトを確認します。

ビルドとは何ですか?

ビルドシステムは、ソースコードを実行可能なアプリケーションに変換します。ビルドでは、多くの場合、アプリケーションまたはライブラリの分析、コンパイル、リンク、パッケージ化に複数のツールを使用します。Gradle は、タスクベースのアプローチを使用してこれらのコマンドを整理して実行します。

タスクは、入力を出力に変換するコマンドをカプセル化します。プラグインはタスクとその構成を定義します。プラグインをビルドに適用すると、そのタスクが登録され、その入力と出力を使用して接続されます。たとえば、Android Gradle プラグイン(AGP)をビルドファイルに適用すると、APK または Android ライブラリのビルドに必要なすべてのタスクが登録されます。java-library プラグインを使用すると、Java ソースコードから jar をビルドできます。Kotlin や他の言語にも同様のプラグインがありますが、他のプラグインはプラグインを拡張することを目的としています。たとえば、protobuf プラグインは、AGP や java-library などの既存のプラグインに protobuf サポートを追加することを目的としています。

Gradle は構成よりも規則を優先するので、プラグインにはすぐに使える適切なデフォルト値が設定されますが、宣言型のドメイン固有言語(DSL)を使用してビルドをさらに構成することもできます。DSL は、ビルド方法ではなく、ビルドする内容を指定できるように設計されています。プラグインのロジックが「どのように」を管理します。この構成は、プロジェクト(およびサブプロジェクト)内の複数のビルドファイルで指定します。

タスク入力には、ファイルやディレクトリのほか、Java 型(整数、文字列、カスタムクラス)としてエンコードされたその他の情報も使用できます。出力には、ディスクに書き込む必要があるディレクトリまたはファイルのみを指定できます。タスク出力を別のタスク入力に接続すると、タスクがリンクされ、一方が他方よりも先に実行されるようになります。

Gradle ではビルドファイルで任意のコードやタスク宣言を記述できますが、その場合、ツールがビルドを理解し、保守するのが難しくなる可能性があります。たとえば、プラグイン内のコードのテストは作成できますが、ビルドファイル内のコードのテストは作成できません。代わりに、ビルドロジックとタスク宣言を(自分または他のユーザーが定義する)プラグインに限定し、そのロジックをビルドファイルでどのように使用するかを宣言する必要があります。

Gradle ビルドを実行するとどうなりますか?

Gradle ビルドは 3 つのフェーズで実行されます。これらの各フェーズでは、ビルドファイルで定義したコードの異なる部分が実行されます。

  • 初期化では、ビルドに含まれるプロジェクトとサブプロジェクトが決定され、ビルドファイルと適用されたプラグインを含むクラスパスが設定されます。このフェーズでは、ビルドするプロジェクトと、プラグインとライブラリを取得する場所を宣言する設定ファイルに焦点を当てます。
  • 構成は、プロジェクトごとにタスクを登録し、ビルドファイルを実行してユーザーのビルド仕様を適用します。実行中に生成されたデータやファイルには構成コードがアクセスできないことを理解することが重要です。
  • 実行: アプリケーションの実際の「ビルド」を行います。構成の出力はタスクの有向非巡回グラフ(DAG)です。これは、ユーザーがリクエストしたすべての必要なビルドステップを表します(コマンドラインで指定されたタスク、またはビルドファイルのデフォルトとして指定されたタスク)。このグラフは、タスク間の依存関係を表します。これは、タスクの宣言で明示的に指定されている場合もあれば、入力と出力に基づいて指定されている場合もあります。タスクの入力が別のタスクの出力である場合、そのタスクは他のタスクの後に実行する必要があります。このフェーズでは、グラフで定義された順序で古いタスクを実行します。タスクの入力が前回の実行から変更されていない場合、Gradle はタスクをスキップします。

詳細については、Gradle のビルド ライフサイクルをご覧ください。

構成 DSL

Gradle は、ドメイン固有言語(DSL)を使用してビルドを構成します。この宣言型のアプローチは、段階的な(命令型の)指示を書くのではなく、データを指定することに重点を置いています。ビルドファイルは Kotlin または Groovy を使用して記述できますが、Kotlin を使用することを強くおすすめします。

DSL は、ドメイン エキスパートやプログラマなど、すべてのユーザーがプロジェクトに簡単に貢献できるように、データをより自然な方法で表す小さな言語を定義します。Gradle プラグインは DSL を拡張して、タスクに必要なデータを設定できます。

たとえば、ビルドの Android 部分の構成は次のようになります。

Kotlin

android {
    namespace = "com.example.app"
    compileSdk = 34
    // ...

    defaultConfig {
        applicationId = "com.example.app"
        minSdk = 34
        // ...
    }
}

Groovy

android {
    namespace 'com.example.myapplication'
    compileSdk 34
    // ...

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdk 24
        // ...
    }
}

背後では、DSL コードは次のようになります。

fun Project.android(configure: ApplicationExtension.() -> Unit) {
    ...
}

interface ApplicationExtension {
    var compileSdk: Int
    var namespace: String?

    val defaultConfig: DefaultConfig

    fun defaultConfig(configure: DefaultConfig.() -> Unit) {
        ...
    }
}

DSL 内の各ブロックは、ラムダを受け取って構成し、同じ名前のプロパティを使用してアクセスする関数で表されます。これにより、ビルドファイル内のコードがデータ仕様のように見えます。

外部依存関係

Maven ビルドシステムでは、依存関係の仕様、ストレージ、管理システムが導入されました。ライブラリはリポジトリ(サーバーまたはディレクトリ)に保存され、バージョンや他のライブラリへの依存関係などのメタデータが含まれます。検索するリポジトリと使用する依存関係のバージョンを指定すると、ビルドシステムがビルド中にそれらをダウンロードします。

Maven アーティファクトは、グループ名(会社、デベロッパーなど)、アーティファクト名(ライブラリの名前)、そのアーティファクトのバージョンで識別されます。通常、これは group:artifact:version で表されます。

このアプローチにより、ビルド管理が大幅に改善されます。このようなリポジトリは「Maven リポジトリ」と呼ばれることがよくありますが、これはアーティファクトのパッケージ化と公開方法に関係しています。これらのリポジトリとメタデータは、Gradle を含む複数のビルドシステムで再利用されています(Gradle はこれらのリポジトリに公開できます)。公開リポジトリでは、すべてのユーザーが共有でき、会社のリポジトリは内部依存関係を社内に保持します。

また、プロジェクトを依存関係として使用できるサブプロジェクト(Android Studio では「モジュール」とも呼ばれます)にモジュール化することもできます。各サブプロジェクトは、サブプロジェクトまたはトップレベル プロジェクトで消費できる出力(jar など)を生成します。これにより、再ビルドが必要な部分を分離し、アプリケーション内の責任をより明確に分離することで、ビルド時間を短縮できます。

依存関係の指定方法については、ビルド依存関係を追加するで詳しく説明します。

ビルド バリアント

Android アプリを作成する場合、通常は複数のバリアントをビルドします。バリアントには異なるコードが含まれているか、異なるオプションでビルドされており、ビルドタイプとプロダクト フレーバーで構成されています。

ビルドタイプは、宣言されたビルド オプションによって異なります。デフォルトでは、AGP は「リリース」と「デバッグ」のビルドタイプを設定しますが、これらを調整して追加することもできます(ステージングまたは内部テストの場合など)。

デバッグビルドでは、アプリを圧縮したり難読化したりしないため、ビルドが高速化され、すべてのシンボルがそのまま保持されます。また、アプリを「デバッグ可能」としてマークし、汎用デバッグ キーで署名して、デバイスにインストールされているアプリファイルへのアクセスを有効にします。これにより、アプリケーションの実行中にファイルやデータベースに保存されたデータを探索できます。

リリースビルドでは、アプリが最適化され、リリースキーで署名され、インストールされたアプリファイルが保護されます。

プロダクト フレーバーを使用すると、アプリケーションに含まれるソースと依存関係のバリエーションを変更できます。たとえば、アプリケーション用に「デモ」と「完全」のフレーバーを作成したり、「無料」と「有料」のフレーバーを作成したりできます。共通のソースを「main」ソースセット ディレクトリに記述し、フレーバーの名前が付けられたソースセット内のソースをオーバーライドまたは追加します。

AGP は、ビルドタイプとプロダクト フレーバーの組み合わせごとにバリアントを作成します。フレーバーを定義しない場合、バリアントはビルドタイプの名前が付けられます。両方を定義すると、バリアントの名前は <flavor><Buildtype> になります。たとえば、ビルドタイプが releasedebug、フレーバーが demofull の場合、AGP はバリアントを作成します。

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

次のステップ

ビルドのコンセプトを確認したので、プロジェクトの Android ビルド構造について見てみましょう。