Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

ビルド速度を最適化する

ビルドに時間がかかると開発プロセスも滞ります。このページでは、ビルドを遅延させるボトルネックを解消するための方法について説明します。

ビルド速度を改善する一般的なプロセスは次のとおりです。

  1. ビルド構成を最適化する - 数ステップだけで、大半の Android Studio プロジェクトですぐに効果が現れます。
  2. ビルドのプロファイリングを行う - プロジェクトやワークステーションに固有の問題である可能性もある複雑なボトルネックを特定し、診断します。

アプリを開発する際は、可能な限り Android 7.0(API レベル 24)以上を搭載したデバイスにデプロイしてください。新しいバージョンの Android プラットフォームには、Android ランタイム(ART)マルチ DEX ファイルのネイティブ サポートなど、更新内容をアプリにプッシュするための便利な仕組みが実装されています。

注: このページに記載の最適化を一切行わなくても、初回のクリーンビルドに続いてクリーンビルドまたは増分ビルドを行う際に、ビルド速度が大幅に向上する場合があります。これは、他の JVM プロセスと同様、パフォーマンスを改善するための「ウォーミング アップ」タイムが Gradle デーモンに設けられているためです。

ビルド構成を最適化する

以下のおすすめの手法を採用することで、Android Studio プロジェクトのビルド速度を向上させることができます。

常に最新のツールを使用する

Android ツールは、アップデートによりほぼ毎回、ビルドの最適化や新しい機能が追加されます。このページに記載のさまざまな手法は、最新版を使用していることを前提としています。最新の最適化機能を活用するには、以下のツールの最新版を使用してください。

開発用のビルド バリアントを作成する

リリース用のアプリの準備に必要な構成の多くは、アプリの開発段階では必要ありません。不要なビルドプロセスを有効にすると、増分ビルドやクリーンビルドの処理時間が長くなるため、アプリの開発時に必要なビルド構成だけを保持するビルド バリアントを設定してください。「dev」フレーバーと「prod」フレーバー(リリース版の構成用)を作成する例を以下に示します。

android {
  ...
  defaultConfig {...}
  buildTypes {...}
  productFlavors {
    // When building a variant that uses this flavor, the following configurations
    // override those in the defaultConfig block.
    dev {
      // To avoid using legacy multidex when building from the command line,
      // set minSdkVersion to 21 or higher. When using Android Studio 2.3 or higher,
      // the build automatically avoids legacy multidex when deploying to a device running
      // API level 21 or higher—regardless of what you set as your minSdkVersion.
      minSdkVersion 21
      versionNameSuffix "-dev"
      applicationIdSuffix '.dev'
    }

    prod {
      // If you've configured the defaultConfig block for the release version of
      // your app, you can leave this block empty and Gradle uses configurations in
      // the defaultConfig block instead. You still need to create this flavor.
      // Otherwise, all variants use the "dev" flavor configurations.
    }
  }
}

すでにビルド構成内でプロダクト フレーバーを使用してアプリの複数のバージョンを作成している場合は、フレーバー ディメンションを使用することで、各フレーバーを「dev」構成や「prod」構成と組み合わせることができます。たとえば、すでに「demo」と「full」のフレーバーを設定している場合、次の構成例のように、「devDemo」や「prodFull」などの結合フレーバーを作成できます。

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.

  flavorDimensions "stage", "mode"

  productFlavors {
    dev {
      dimension "stage"
      minSdkVersion 21
      versionNameSuffix "-dev"
      applicationIdSuffix '.dev'
      ...
    }

    prod {
      dimension "stage"
      ...
    }

    demo {
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }
  }
}

単一バリアントのプロジェクトの同期を有効にする

ビルド構成とプロジェクトを同期することは、プロジェクトがどのように構成されているかを Android Studio に認識させるうえで重要なステップです。ただし、大規模なプロジェクトでは、この処理に時間がかかることがあります。プロジェクトで複数のビルド バリアントを使用している場合は、現在選択しているバリアントに限定することで、プロジェクトの同期を最適化できます。

この最適化を有効にするには、Android Studio 3.3 以上、または Android Gradle Plugin 3.3.0 以上を使用する必要があります。この最適化機能は、すべてのプロジェクトでデフォルトで有効です。

手動でこの最適化を有効にするには、[File] > [Settings] > [Experimental] > [Gradle](Mac の場合は [Android Studio] > [Preferences] > [Experimental] > [Gradle])をクリックして、[Only sync the active variant] チェックボックスをオンにします。

注: この最適化機能は、Java 言語と C++ 言語を使用したプロジェクトを完全にサポートしており、Kotlin にも一部対応しています。Kotlin コンテンツを含むプロジェクトでこの最適化を有効にすると、内部で全バリアントを使用するように、Gradle 同期がフォールバックします。

不要なリソースのコンパイルを回避する

テスト対象外のリソース(追加の言語ローカライズ リソースや画面密度リソースなど)のコンパイルやパッケージ化を避けてください。そのためには、次の例のように、「dev」フレーバーに対して、1 つの言語リソースと 1 つの画面密度だけを指定します。

android {
  ...
  productFlavors {
    dev {
      ...
      // The following configuration limits the "dev" flavor to using
      // English stringresources and xxhdpi screen-density resources.
      resConfigs "en", "xxhdpi"
    }
    ...
  }
}

デバッグビルドに対して Crashlytics を無効にする

Crashlytics レポート機能を使用する必要がない場合は、次のように、そのプラグインを無効にするとデバッグビルドの速度が向上します。

android {
  ...
  buildTypes {
    debug {
      ext.enableCrashlytics = false
    }
}

また、次のように、アプリ内の Fabric サポートの初期化方法を変更して、デバッグビルドに対してランタイムで Crashlytics Kit を無効にする必要があります。

Kotlin

// Initializes Fabric for builds that don't use the debug build type.
Crashlytics.Builder()
        .core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
        .build()
        .also { crashlyticsKit ->
            Fabric.with(this, crashlyticsKit)
        }

Java

// Initializes Fabric for builds that don't use the debug build type.
Crashlytics crashlyticsKit = new Crashlytics.Builder()
    .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build())
    .build();

Fabric.with(this, crashlyticsKit);

ビルド ID の自動生成を無効にする

デバッグビルドで Crashlytics を使用する場合、増分ビルドを高速化するには、ビルドのたびに固有のビルド ID を使って Crashlytics がアプリリソースを更新するのを防ぐ必要があります。このビルド ID はマニフェストで参照されるリソース ファイルに格納されるため、ビルド ID の自動生成を無効にした場合、デバッグビルド用に Apply Changes を Crashlytics とともに使用することもできるようになります。

Crashlytics がビルド ID を自動的に更新するのを防ぐには、以下の内容を build.gradle ファイルに追加します。

android {
  ...
  buildTypes {
    debug {
      ext.alwaysUpdateBuildId = false
    }
}

Crashlytics を使用した場合のビルドの最適化方法については、公式ドキュメントをご覧ください。

デバッグビルドに静的ビルド構成値を使用する

デバッグビルド タイプのマニフェスト ファイルやリソース ファイルに含まれるプロパティには、必ず静的な値またはハードコーディングした値を使用してください。

たとえば、動的なバージョン コードや、バージョン名、リソース、その他マニフェスト ファイルを変更するビルドロジックを使用していると、実際にはホットスワップで十分な変更の場合でも、変更するたびに APK のフルビルドが必要になります。このような動的プロパティがビルド構成に必要な場合は、リリース用のビルド バリアントに分離して、デバッグビルドで静的な値だけを使用します。build.gradle ファイルの例を以下に示します。

int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full APK build and reinstallation because the AndroidManifest.xml
        // must be updated.
        versionCode 1
        versionName "1.0"
        ...
    }

    // The defaultConfig values above are fixed, so your incremental builds don't
    // need to rebuild the manifest (and therefore the whole APK, slowing build times).
    // But for release builds, it's okay. So the following script iterates through
    // all the known variants, finds those that are "release" build types, and
    // changes those properties to something dynamic.
    applicationVariants.all { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch;
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName;
        }
    }
}

静的依存関係バージョンを使用する

build.gradle ファイル内で依存関係を宣言する場合、バージョン番号の後ろにプラス記号を付ける('com.android.tools.build:gradle:2.+')ことは避けてください。このような動的なバージョン番号を使用すると、想定外のバージョン更新が発生し、バージョン間の差異を解決しづらくなります。また、Gradle が更新をチェックするようになるため、ビルド時間が長くなります。代わりに、静的な値またはハードコーディングした値を使用してください。

オフライン モードを有効にする

低速のネットワークに接続している場合、Gradle がネットワーク リソースを使用して依存関係を解決しようとすると、ビルド時間が長引くことがあります。ネットワーク リソースを使用せずにローカルなキャッシュに保存したアーティファクトだけを使用するように、Gradle に指示できます。

Android Studio を使用してビルドする際に Gradle をオフラインで使用するには:

  1. [File] > [Settings](Mac の場合は [Android Studio] > [Preferences])をクリックして、[Preferences] ウィンドウを開きます。
  2. 左パネルで [Build, Execution, Deployment] > [Gradle] をクリックします。
  3. [Offline work] チェックボックスをオンにします。
  4. [Apply] または [OK] をクリックします。

コマンドラインからビルドしている場合は、--offline オプションを渡します。

ライブラリ モジュールを作成する

アプリ内で、Android ライブラリ モジュールに変換可能なコードを見つけます。このようにコードをモジュール化することで、ビルドシステムは、編集されたモジュールだけをコンパイルし、その出力を将来のビルドに備えてキャッシュに保存できるようになります。これにより、並列プロジェクト実行による最適化を有効にした場合に、その効果が高まります。

カスタム ビルドロジック用のタスクを作成する

ビルド プロファイルを作成した結果、ビルドにかかる時間のうち、「プロジェクトの設定」フェーズに比較的長い時間を要していることが判明した場合は、build.gradle スクリプトを見直して、カスタム Gradle タスクに含めることができるコードがないか探してください。タスクに移行したビルドロジックは、必要なときにだけ実行されるようになり、出力結果がキャッシュに保存され将来のビルドで利用できるようになります。また、移行したビルドロジックは、並列実行できるようになります(並列プロジェクト実行を有効にしている場合)。詳細については、Gradle の公式ドキュメントをご覧ください。

ヒント: 大量のカスタムタスクを含むビルドの場合は、カスタムタスク クラスを作成して、build.gradle ファイルの中身を整理することをおすすめします。project-root/buildSrc/src/main/groovy/ ディレクトリにクラスを追加すると、Gradle によって、プロジェクトのすべての build.gradle ファイルのクラスパス内にそのクラスが自動的に追加されます。

画像を WebP に変換する

WebP という画像ファイル形式には、不可逆圧縮モード(JPEG と同様)と透過モード(PNG と同様)があり、JPEG や PNG よりも圧縮率が優れています。画像ファイルのサイズを削減すると、ビルド時の圧縮が不要になるため、特に大量の画像リソースを使用しているアプリの場合に、ビルド時間が短縮されます。ただし、WebP 画像は、展開する際にデバイスの CPU 使用量が少し増加する場合があります。Android Studio を使用すると、簡単に画像を WebP に変換できます。

PNG 自動最適化を無効にする

PNG 画像を WebP に変換できない場合(あるいは、変換したくない場合)でも、アプリをビルドするたびに自動で画像圧縮を行わないようにすれば、ビルド時間を短縮できます。Android プラグイン 3.0.0 以上を使用している場合、「debug」ビルドタイプに対してのみ、PNG 自動最適化はデフォルトで無効です。他のビルドタイプに対しても PNG 自動最適化を無効にするには、build.gradle ファイルに以下を追加します。

android {
    buildTypes {
        release {
            // Disables PNG crunching for the release build type.
            crunchPngs false
        }
    }

// If you're using an older version of the plugin, use the
// following:
//  aaptOptions {
//      cruncherEnabled false
//  }
}

ビルドタイプ単位やプロダクト フレーバー単位でこのプロパティは定義できないため、アプリのリリース版をビルドする際は、手動でこのプロパティを true に設定する必要があります。

ビルド キャッシュを有効にする

ビルド キャッシュとは、Android Plugin for Gradle がプロジェクトをビルドするときに生成する特定の出力(非パッケージ化 AAR、事前 DEX 変換済みのリモート依存関係など)を保存する機能です。キャッシュに保存されたファイルは、ビルドシステムが以後のビルドを行う際に再利用できるため、再生成の手間が省け、クリーンビルドの時間が大幅に短縮されます。

Android プラグイン 2.3.0 以上を使用している新しいプロジェクトの場合、明示的にビルド キャッシュを無効にしない限り、デフォルトで有効です。詳細については、ビルド キャッシュによるクリーンビルドの高速化をご覧ください。

増分アノテーション プロセッサを使用する

Android Gradle プラグイン 3.3.0 以上で、増分アノテーション処理のサポートが改善されています。そのため、増分ビルド速度を向上させるには、Android Gradle プラグインを更新して、可能な限り増分アノテーション プロセッサだけを使用してください。

注: この機能は、Gradle 5.1 を除く Gradle バージョン 4.10.1 以上で利用できます(Gradle の問題 #8194 を参照)。

まず、増分アノテーション処理をサポートする一般的なアノテーション プロセッサを示した下記の表をご覧ください。より詳細なリストについては、一般的なアノテーション プロセッサのサポートの状態をご覧ください。アノテーション プロセッサによっては、最適化を有効にするために追加手順が必要となる場合があります。各アノテーション プロセッサのドキュメントをご確認ください。

また、Kotlin を使用するアプリの場合、kapt 1.3.30 以上を使用して、Kotlin コード向けの増分アノテーション プロセッサをサポートする必要があります。この動作を手動で有効にする必要があるかどうかについては、公式ドキュメントをご覧ください。

増分ビルドをサポートしていないアノテーション プロセッサを使用している場合、アノテーション処理は増分になりません。ただし、プロジェクトで kapt を使用している場合、Java コンパイルは引き続き増分になります。

増分アノテーション プロセッサのサポート

プロジェクト名アノテーション プロセッサのクラス名サポート対象
DataBindingandroid.databinding.annotationprocessor.ProcessDataBindingAGP 3.5
Roomandroidx.room.RoomProcessor2.3.0-alpha02

2.20: room.incremental オプションを使用します
ButterKnifebutterknife.compiler.ButterKnifeProcessor10.2.0
Glidecom.bumptech.glide.annotation.compiler.GlideAnnotationProcessor4.9.0
Daggerdagger.internal.codegen.ComponentProcessor2.18
Lifecycleandroidx.lifecycle.LifecycleProcessor2.2.0-alpha02
AutoServicecom.google.auto.service.processor.AutoServiceProcessor1.0-rc7
Daggerdagger.android.processor.AndroidProcessor2.18
Realmio.realm.processor.RealmProcessor5.11.0
Lomboklombok.launch.AnnotationProcessorHider$AnnotationProcessor1.16.22
Lomboklombok.launch.AnnotationProcessorHider$ClaimingProcessor1.16.22

ビルドのプロファイリングを行う

大規模なプロジェクトや、カスタム ビルドロジックを多数実装したプロジェクトの場合、ビルドプロセスを詳細に調べて、ボトルネックを特定する必要があります。そのためには、ビルド ライフサイクルの各フェーズや各ビルドタスクを Gradle が実行するのに要する時間をプロファイリングします。ビルド プロファイルによって、たとえば、Gradle がプロジェクトを設定する際に長い時間を要していると判明した場合は、カスタム ビルドロジックを設定フェーズから移行するなどの対処が必要になります。また、ビルド時間の大半を mergeDevDebugResources タスクが占めている場合は、画像を WebP に変換するか、PNG 自動最適化を無効にする必要があると考えられます。

Android Studio 4.0 以上を使用している場合は、Build Analyzer を使用することで、ビルドのパフォーマンスに関する問題を調査できます。

コマンドラインからビルドのプロファイリングを行う

Android Studio を使用していない場合、一般的に、ビルド時間のトラブルシューティングを行うには、プロファイリングを有効にして、コマンドラインからビルドし、ビルド構成を微調整して、何度かプロファイリングを行い、変更による効果を検証します。

ビルド プロファイルを生成して表示するには:

  1. プロジェクトのルートでコマンドライン ターミナルを開きます。
  2. 次のコマンドを入力して、クリーンビルドを行います。ビルドのプロファイリングを行う際は、プロファイリング対象のビルドの実行前に、毎回クリーンビルドを行ってください。タスクへの入力(ソースコードなど)に変更がないと、Gradle はタスクをスキップしてしまいます。続けて 2 回ビルドをすると、入力に変更がないためタスクが再実行されず、必然的に速度が上がってしまいます。したがって、確実にフル ビルドプロセスでプロファイリングを行うには、ビルドの前に clean タスクを実行してください。
    // On Mac or Linux, run the Gradle wrapper using "./gradlew".
    gradlew clean
    
  3. 以下のフラグを付けて、「dev」フレーバーなど、プロダクト フレーバーのいずれかのデバッグビルドを行います。
    gradlew --profile --offline --rerun-tasks assembleFlavorDebug
    
    • --profile: プロファイリングを有効にします。
    • --offline: Gradle によるオンライン依存関係の取得を無効にします。これにより、Gradle が依存関係の更新を試みる際の遅延がプロファイリングに及ぼす影響を回避できます。Gradle が依存関係をダウンロードしてキャッシュに保存した状態にするには、プロジェクトを先に 1 回ビルドしておきます。
    • --rerun-tasks: Gradle がすべてのタスクを再実行するように強制し、タスクの最適化は行いません。
  4. 図 1. プロファイル レポートの場所を示すプロジェクト ビュー

    ビルドが完了したら、[Project] ウィンドウを使用して、project-root/build/reports/profile/ ディレクトリに移動します(図 1 を参照)。

  5. profile-timestamp.html ファイルを右クリックして、[Open in Browser] > [Default] を選択します。図 2 のようなレポートが表示されます。レポートの各タブで、ビルドの情報を確認できます。たとえば、[Task Execution] タブには、Gradle が各ビルドタスクを実行する際に要した時間が表示されます。

    図 2. ブラウザでレポートを表示する

  6. (省略可)プロジェクトやビルド構成を変更する前に、ステップ 3 のコマンドから --rerun-tasks フラグを削除して、もう一度コマンドを実行します。Gradle は、入力に変更のないタスク(図 3 のように、レポートの [Task Execution] タブに UP-TO-DATE と表示されているタスク)の実行を省いてビルド時間の短縮を図るため、不必要に実行されているタスクがないか確認できます。たとえば、:app:processDevUniversalDebugManifestUP-TO-DATE のマークがない場合、現在のビルド構成で、このマニフェスト ファイルはビルドのたびに動的に更新されている可能性があります。ただし、:app:checkDevDebugManifest などの一部のタスクは、ビルドのたびに実行する必要があります。

    図 3. タスクの実行結果を表示する

ビルド プロファイル レポートが手に入ったので、レポートの各タブの情報を調べて、最適化できる部分がないか探すことができます。ビルド設定の一部は、プロジェクトやワークステーションによって効果が異なることがあるので、さまざまな設定を試す必要があります。たとえば、大規模なコードベースを含むプロジェクトの場合、コードを圧縮して、不要なコードを除去し、APK サイズを縮小すると効果が得られます。他方、小規模なプロジェクトの場合は、コード圧縮を無効にしたほうが効果が大きいこともあります。また、メモリの少ないマシンで org.gradle.jvmargs を使用して Gradle のヒープサイズを増やすと逆効果になることがあります。

ビルド構成を変更したら、上記の手順を繰り返して、新しいビルド プロファイルを生成し、変更の効果を確認します。このページで説明した基本的な最適化手法を、同じサンプルアプリに一部適用したレポートの例を図 4 に示します。

図 4. ビルド速度を最適化した後の新しいレポートを表示する

ヒント: 強力なプロファイリング ツールが必要な場合は、Gradle のオープンソース プロファイラを使用することをおすすめします。