方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

【Android】Flutterにおけるbuild.gradleの役割と設定方法

この記事は約19分で読めます。

はじめに

現在、方言を話すおしゃべり猫型ロボット「ミーア」を開発中。

https://mia-cat.com

今まで、ミーアのアプリは、iOSのシミュレーターと実機でのみ動作確認をしていたが、Android emulatorでももちろん確認しないといけない。

対応していくうちに、「そもそもbuild.gradleって何?」となったので、基本をおさらい。

Gradleとは何か

Gradleとは、Groovy(またはKotlin)をベースにしたビルド自動化システム。ソフトウェアのコンパイル、パッケージング、テスト、デプロイなど、開発プロセスの自動化を担う。

https://docs.gradle.org/current/userguide/userguide.html

ビルドスクリプトはDSL(ドメイン固有言語:特定の問題領域(ドメイン)に特化したプログラミング言語や仕様言語)を用いて記述されるので、ビルドプロセスの高度なカスタマイズを可能にする。DSLを通じて、依存関係の管理、ビルドタスクの定義、アプリケーションのパッケージングなどを行える。設定は、build.gradle に書く。

Flutterアプリでは、Dartで記述されたコードがネイティブバイナリにコンパイルされる。FlutterプロジェクトにおいてGradleは、最終的なアプリケーションパッケージ(APK)を生成する際、ネイティブ部分のコンパイルや各種リソースの統合、依存関係の解決などを行う。

ちなみに、”Gradle”の名前は「Gradual」(徐々に)と「Build」(ビルド)の組み合わせから来ている。つまり、Gradleは段階的にビルドを進めることができるビルドシステムを意味している。

Flutterプロジェクトのbuild.gradleファイル

Flutterプロジェクトには、通常2つのbuild.gradleファイルが存在する。

  • プロジェクトレベル:Androidアプリ全体の設定を行う。
  • アプリレベルのファイル:アプリモジュールの設定を行う。

階層構造は下記

└── MyApp/  # Project
    ├── gradle/
    │   └── wrapper/
    │       └── gradle-wrapper.properties
    ├── build.gradle(.kts)  #プロジェクトレベルのgradleファイル
    ├── settings.gradle(.kts)
    └── app/  # Module
        ├── build.gradle(.kts)  #アプリレベルのgradleファイル
        └── build/
            ├── libs/
            └── src/
                └── main/  # Source set
                    ├── java/
                    │   └── com.example.myapp
                    ├── res/
                    │   ├── drawable/
                    │   ├── values/
                    │   └── ...
                    └── AndroidManifest.xml

Flutter3.16以降のgradle記載方法の変更点

ちなみに、このプロジェクトレベルとアプリレベルのgradleファイルの記載方法だが、Flutter3.16以降で大きな変更があった。

  • Flutter 3.16では、Gradleの宣言的なplugins {}ブロック(Plugin DSLとも呼ばれる)を使ってこれらのプラグインを適用するサポートが追加され、こちらが推奨される記載方法になった。
  • Android Gradle Plugin(AGP)とKotlinのバージョン指定がbuild.gradleからsettings.gradleに移動した。

詳しくは下記参照。

https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply

というわけで、setting.gradle→build.gradle(project)→build.gradle(app)の順に見ていく。

android/settings.gradle:Gradle設定ファイル

この設定ファイルはルートプロジェクトディレクトリにあり、プロジェクトレベルのリポジトリ設定を定義し、アプリのビルド時に含めるモジュールを Gradle に伝える。

  • プラグイン管理 (pluginManagement): Flutter SDKパスとプロジェクトで使用するリポジトリ(google(), mavenCentral()など)を設定。
  • プラグイン定義 (plugins): 必要なGradleプラグイン(Flutterプラグイン、Androidプラグイン、Kotlinプラグインなど)を宣言的に定義。バージョン管理もここで行い、apply false を指定して自動適用を防ぐ。
  • モジュールのインクルード (include ":app"): ビルド対象のモジュールを指定。
Dart
pluginManagement {
    def flutterSdkPath = {
        def properties = new Properties()
        file("local.properties").withInputStream { properties.load(it) }
        def flutterSdkPath = properties.getProperty("flutter.sdk")
        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
        return flutterSdkPath
    }()

    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")

    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "7.4.2" apply false
    id "org.jetbrains.kotlin.android" version "1.9.20" apply false
    id "com.google.gms.google-services" version "4.3.14" apply false
}

include ":app"

プロジェクトレベルのbuild.gradle(/project_root/android/build.gradle)

プロジェクト全体のビルド設定を定義する。

  • リポジトリ定義 (allprojects { repositories { ... } }): 全プロジェクト共通のリポジトリを定義。
  • クリーンタスク (tasks.register("clean", Delete) { ... }): clean タスクを定義し、プロジェクトのビルドディレクトリを削除。
  • AGPやKotlinのバージョン定義が削除され、これらはsettings.gradleに移行。
Dart
// /project_root/android/build.gradle
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

tasks.register("clean", Delete) {
    delete rootProject.buildDir
}

アプリレベルのbuild.gradle(/project_root/android/app/build.gradle)

特定のアプリケーションモジュール(通常はFlutterアプリ自体)に対する設定を管理する。

主な責務

  • プラグインの適用 (plugins { ... }): 必要なプラグインを宣言的に適用。ここではプラグインを実際にアクティブにする。
  • アプリ固有の設定 (android { ... }): SDKのバージョン、ターゲットバージョン、依存関係など、アプリ固有の設定を行う。
  • 依存関係の管理 (dependencies { ... }): アプリレベルで必要な依存ライブラリを管理。
Dart
plugins {
    id 'com.android.application'
    id 'com.google.gms.google-services'
    id 'kotlin-android'
    id "dev.flutter.flutter-gradle-plugin"
}


android {
    compileSdkVersion 34 // コンパイルに使用するSDKバージョン
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        applicationId "${dartDefines.appId}" //アプリの一意の識別子。Google Playストアなどのアプリストアでアプリを一意に識別するために使用される
        minSdkVersion 26 // サポートする最小のAndroidバージョン
        targetSdkVersion flutter.targetSdkVersion // アプリが最適化されていると想定するAndroidバージョン
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        resValue "string", "app_name", "${dartDefines.appName}"
        archivesBaseName "${dartDefines.flavor}-${flutterVersionName}"
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

flutter {
    source '../..'
}

この、アプリレベルのbuild.gradleファイル名で記述されるアプリ固有の設定 (SDKのバージョン、ターゲットバージョン、依存関係など)について、より詳しく見ていく。

SDKバージョンとAPIレベルの設定(アプリレベル)

  • SDKバージョンとAPIレベルの設定はアプリ単位で行うため、これらの設定はapp/build.gradleファイルに記述する。
  • compileSdkVersion: コンパイル時に使用されるAPIレベル。、ソースコードのコンパイル時に使用可能な Android API と Java API が決まる。最新の Android 機能を使用するには、コンパイル時に最新の Android SDK を使用する。
  • minSdkVersion: アプリがインストール可能な最低限のAndroidバージョン(APIレベル)。この値を設定することで、より古いバージョンのデバイスではアプリがインストールさえできないように制限できる。
  • targetSdkVersion: アプリが最適化およびテストされている目標のAndroidバージョン(APIレベル)を指定する。compileSdkVersionがアプリが利用可能なAPIの最大セットを定義するため、targetSdkVersionは必ずそれ以下、または同じ値でなければないが、compileSdk と targetSdk の値を同じにする必要はない。targetSdk は compileSdk 以下で指定する。

Flutterの場合、compileSdkVersionflutter.compileSdkVersionのように指定して、Flutter SDKによって提供されるバージョンに動的に設定されるようにするのが一般的。

Dart
 defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.myapp"
        // You can update the following values to match your application needs.
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
        minSdkVersion 26
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

この場合、targetSdkVersionの値を確認するには、以下の手順を実行する。

local.propertiesファイルでflutter.sdkの値を確認

project_root/android配下にあるlocal.propertiesファイルを開き、flutter.sdkの値を確認する。これはFlutter SDKがインストールされているディレクトリを指す。

Flutter SDK内のflutter.groovyファイルを開く

/usr/local/Caskroom/flutter/3.7.12/flutter/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy を開く。

  • compileSdkVersion = 34
  • targetSdk Version = 33

が設定されていることが分かった。

ちなみに今回の記事の趣旨からはズレるが、compileSdkVersionで設定されているAPIレベルに相当するエミュレーターをインストールするには、

Android Studioで、Toolsタブ→SDK Managerをクリックして、”SDK Platforms”タブを選択し、相当するAPIレベルを選択する(今回だとAPI34)。その後、画面右上のDevice Managerアイコンをクリックして、emulatorを追加する。

gradle-wrapper.properties

このファイルは、Gradle Wrapperに関する設定が書かれており、プロジェクトのビルドに使用するGradleのバージョンを指定する。Gradle Wrapperは、開発者が自分のマシンにGradleをインストールしていなくても、指定されたバージョンのGradleを自動的にダウンロードし、プロジェクトのビルドを行えるようにするもの。

gradle-wrapper.propertiesファイル内の設定例。

  • distributionUrl はダウンロードするGradleのバージョンとタイプ(binまたはall)を指定する。
  • 他のパラメータは、ダウンロードしたファイルの保存場所や方法を定義。
Dart
// /project_root/android/gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

GradleバージョンとJava, KotlinのCompatibilityはこちら
https://docs.gradle.org/current/userguide/compatibility.html

Gradleのビルドキャッシュクリアと、再ビルド

Androidエミュレーターでビルドする時に、エラーが生じた場合で、指摘されているエラーがコード上は問題ない場合はビルドキャッシュの問題の可能性がある。

your-flutter-project/android/ ディレクトリで、gradleのビルドをキャッシュした後に、プロジェクトを再ビルドする。

ShellScript
./gradlew clean
./gradlew build

# つなげて記載も可能
./gradlew clean build

ビルド時にエラー詳細などを見たいときは、下記オプションをつける。

  • --stacktrace または -S:エラーの原因を特定するためにスタックトレースを表示
  • --info または -i:ビルドプロセスの詳細情報を提供
  • --debug:さらに詳細なデバッグ情報を提供

flutterパッケージを追加する時

何かしら新しい機能をFlutterアプリに追加する場合、まずは、パッケージのReadmeをよく読む。

例えば、バックグラウンドでアプリ操作をしたい場合に、flutter_background_serviceというflutterパッケージを導入したいとする。

https://pub.dev/packages/flutter_background_service

まず、Readmeを読むと、Androidの場合のWarningとして下記が記載されている。

Dart
WARNING:

Please make sure your project already use the version of gradle tools below:

in android/build.gradle classpath 'com.android.tools.build:gradle:7.4.2'
in android/build.gradle ext.kotlin_version = '1.8.10'
in android/gradle/wrapper/gradle-wrapper.properties distributionUrl=https://services.gradle.org/distributions/gradle-7.5-all.zip

flutter_background_serviceパッケージを使用する際に、互換性を保つために推奨される設定をしないまま開発すると、実際に動作確認の際に「あれ、動かない!?なんで」となって原因究明に時間を費やす羽目になる。

ローカルホスト上のサーバーとの通信には、10.0.2.2を指定

Androidエミュレータを使用してローカルホスト上で動作しているサーバーと通信する際には、通常の localhost アドレス (127.0.0.1) を使用することはできない。これは、エミュレータが実行される環境がホストマシンと異なるため。

10.0.2.2を指定することで、エミュレータからホストマシン上で実行されているサーバー(APIサーバー、データベースサーバーなど)への接続が可能になる。

例えば、ホストマシン上で8080ポートでAPIサーバーが動作している場合、エミュレータ上のアプリケーションからこのAPIサーバーに接続するためのURLは次のようになる。このIPアドレスはAndroidエミュレータ専用であり、Android実機でのテストでは異なる設定が必要になる。

Dart
API_URL=http://10.0.2.2:8080

FlutterアプリをAndroid実機でビルドする時のセットアップに関しては、こちら。

まとめ:iOSとAndroidの設定の違い

Androidのビルド設定

今まで見てきたように、Androidアプリ開発では、build.gradleファイルが中心的な役割を果たす。build.gradleファイルはDSL(ドメイン固有言語)を使用しており、開発者はこのスクリプトを通じてアプリのビルドプロセスを細かく制御できる。

iOSのビルド設定

一方、iOS開発環境では、build.gradleに相当する直接的なファイルは存在しない。

iOSアプリのビルド設定は、主にXcodeのGUIを通じて行われる。Xcodeプロジェクト内の.pbxprojファイルにはビルド設定情報が含まれているが、このファイルはXcode独自のフォーマットであり、手動での編集は推奨されていない。

なので、iOSの場合はXcodeのGUIに慣れることを要求され、ビルドプロセスのカスタマイズにおいては、Androidのようなスクリプトベースのアプローチに比べて柔軟性が制限されている。

コメント

タイトルとURLをコピーしました