lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

ビルド速度の最適化

ビルドに時間がかかると開発プロセスも滞ります。このページではビルドを遅延させるボトルネックを解消するためのテクニックをいくつかご紹介します。

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

  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"
      ...
    }
  }
}

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

テスト対象外のリソースのコンパイルやパッケージ化は避けましょう(追加の言語ローカライズ リソースや画面密度リソースなど)。そのためには、次の例のように単純に、"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 を無効にする必要があります。

// 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);

デバッグビルドで Crashlytics を使用しつつも増分ビルドの速度を上げるには、Crashlytics がビルドのたびに変わるビルド ID を参照してアプリのリソースを更新するのを防ぎます。Crashlytics のビルド ID が毎回更新されないようにするには、次の内容を build.gradle ファイルに追加します。

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

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

デバッグビルドに静的なビルド コンフィグレーションを使用

デバッグビルド タイプで使用するマニフェスト ファイルやリソース ファイルに含まれるプロパティには、必ず静的またはハードコーディングした値を使用してください。ビルドのたびにマニフェスト ファイルやアプリ リソース内の値を更新しなければならない場合、Instant Run でコールド スワップが行えないため、新たに APK のビルドとインストールが必要になります。

たとえば動的なバージョン コード、バージョン名、リソース、およびマニフェスト ファイルの変更が必要なその他のビルドロジックを使用すると、変更内容を実行するたびに 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 (which is not supported by Instant Run).
        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 オプションをつけます。

オンデマンド設定の有効化

ビルドシステムでは、Gradle でアプリのビルド方法を認識できるように、必ずプロジェクト内のすべてのモジュールとその依存先の設定をしてからビルドを実施します(実際には 1 つのモジュールのビルドとテストしか実施しない場合でも)。そのため、大規模なマルチ モジュール プロジェクトでは、ビルド処理に長い時間がかかります。ビルド対象のモジュール設定のみを行うよう Gradle に指定するには、次の手順で 「オンデマンド設定」を有効にします。

  1. [File] > [Settings](Mac では [Android Studio] > [Preferences])をクリックして [Preferences] ウィンドウを開きます。
  2. 左のペインで [Build, Execution, Deployment] > [Compiler] をクリックします。
  3. [Configure on demand] チェックボックスをオンにします。
  4. [Apply] または [OK] をクリックします。

ライブラリ モジュールの作成

アプリ内で Android ライブラリ モジュール に変換可能なコードを見つけます。このようにコードをモジュール化すると、ビルドシステムで修正したモジュールのみをコンパイルし、その出力を以降のビルドのためにキャッシュすることができます。これにより、オンデマンド設定並行プロジェクト実行の効果が高まります(これらの機能を有効にしている場合)。

カスタムビルド ロジックのタスク作成

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

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

dexOptions の設定とライブラリの事前 DEX 変換の有効化

Android プラグインで提供される dexOptions ブロックで次の DEX ビルド プロパティを設定すると、ビルド速度の向上が期待できます。

  • preDexLibraries:増分ビルドを高速化するために、依存ライブラリを事前 DEX 変換するかどうかを宣言します。この機能によってクリーンビルドの速度は落ちる可能性があるため、継続的インテグレーション サーバー側ではこの機能を無効にすることをお勧めします。
  • maxProcessCount:dex-in プロセスの実行中に使用する最大スレッド数を指定します。デフォルトは 4 です。
  • javaMaxHeapSize:DEX コンパイラ用の最大ヒープサイズを指定します。このプロパティを指定しないと、(dex-in プロセスが有効な場合に DEX コンパイラと共有される)Gradle のヒープサイズが増加します。

次に例を示します。

android {
  ...
  dexOptions {
    preDexLibraries true
    maxProcessCount 8
    // Instead of setting the heap size for the DEX process, increase Gradle's
    // heap size to enable dex-in-process. To learm more, read the next section.
    // javaMaxHeapSize "2048m"
  }
}

これらの設定値を大きくして、その効果をビルドのプロファイルで確認してください。プロセスにリソースを割り当てすぎると、パフォーマンスの低下が見られる場合もあります。

注: 既に Gradle のデーモンが実行中の場合は、そのプロセスを停止してから、新しい設定値で初期化する必要があります。Gradle のデーモンを終了するには、[View] > [Tool Windows] > [Terminal] を選択して、gradlew --stop というコマンドを入力します。

Gradle ヒープサイズの増加と dex-in プロセスの有効化

dex-in プロセスでは、DEX コンパイラは別の VM プロセスではなく、ビルドプロセスで実行されます。これにより増分ビルドおよびクリーンビルドの速度が上がります。Android Studio 2.1 以降でプロジェクトを新規作成すると、ビルドプロセスでこの機能を有効化するのに十分なメモリ量がデフォルトで割り当てられます。Android Studio 2.1 以降でプロジェクトを作成していない場合は、Gradle デーモンの最大ヒープサイズを 1536 MB 以上に設定する必要があります。次の例では、プロジェクトの gradle.properties ファイルで Gradle のヒープサイズを 2048 MB に設定しています。

org.gradle.jvmargs = -Xmx2048m

大規模なプロジェクトでは、これより大きい Gradle ヒープサイズでも効果が出る可能性があります。ただし、メモリの少ないマシンを使用している場合は、メモリの使用量を減らすために IDE の設定が必要になることがあります。IDE に割り当てるリソース量の変更方法と Gradle によるビルド パフォーマンスへの影響については、ビルドのプロファイルのセクションをご覧ください。

注: 既に Gradle のデーモンが実行中の場合は、そのプロセスを停止してから、新しい設定値で初期化する必要があります。Gradle のデーモンを終了するには、[View] > [Tool Windows] > [Terminal] を選択して、gradlew --stop というコマンドを入力します。

モジュールの build.gradle ファイルで android.dexOptions.javaMaxHeapSize の値を定義している場合は、その値によって DEX コンパイラのヒープサイズが制御されます。Gradle のヒープサイズは javaMaxHeapSize プロパティで設定した値よりも 512 MB 大きく、かつ 1536 MB 以上になるように設定する必要があります。たとえば javaMaxHeapSize を 1280 MB に設定した場合は、org.gradle.jvmargs を 1792 MB(1280 + 512)以上に設定しなければなりません(ただし、より大きなヒープサイズが理想的です)。dex-in プロセスを有効化するために javaMaxHeapSize の値を指定する必要はありません。ビルド設定に javaMaxHeapSize を含めない場合は、Gradle のヒープサイズが 1536 MB 以上であることだけを確認してください。

画像の WebP 変換

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

PNG 変換の無効化

PNG 画像を WebP に変換できない(または、したくない)場合、アプリをビルドするたびに自動で画像圧縮を行わないようにすれば、ビルドの速度を上げることができます。この最適化を無効にするには、build.gradle ファイルに次の内容を追加します。

android {
  ...
  aaptOptions {
    cruncherEnabled false
  }
}

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

Instant Run の有効化

Instant Run を使用すると、新たに APK をビルドすることなく(場合によっては現在の activity の再起動も不要)特定のコードやリソースの変更点を反映できるため、アプリの更新時間を大幅に短縮できます。アプリを変更して [Apply Changes] をクリックすると、Instant Run が実行されます。 Instant Run をデフォルトで有効にする方法は次のとおりです。

  • デバッグビルド バリアントを使用してアプリをビルドする。
  • バージョン 2.3.0 以上の Android Plugin for Gradle を使う。
  • アプリのモジュール レベルの build.gradle ファイルで minSdkVersion を 15 以上に設定する。
  • Android 5.0(API レベル 21)以上を実行している端末にアプリをデプロイして、[Run] をクリックする

上記の条件を満たしているにもかかわらず、ツールバーに [Apply Changes] ボタンが表示されない場合は、次の手順に従って IDE 設定で Instant Run が無効になっていないか確認してください。

  1. [Settings] または [Preferences] ダイアログを開きます。
  2. [Build, Execution, Deployment] > [Instant Run] を選択します。
  3. [Enable Instant Run] がオンになっていることを確認します。

ビルドキャッシュの有効化

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

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

アノテーション プロセッサの無効化

アノテーション プロセッサを使用すると、増分 Java コンパイルが無効になります。前回のビルドから変更のあったクラスのみをコンパイルすることによる効果を得るには、できるだけアノテーション プロセッサを無効にすることをお勧めします。

ビルドのプロファイル

大規模なプロジェクトやカスタムビルド ロジックを多く使用するプロジェクトでは、ビルドプロセスを詳しく調査してボトルネックを特定する必要があります。ボトルネックを特定するには、ビルドサイクルの各フェーズおよび各ビルドタスクを Gradle で実行するのに要する時間をプロファイルします。ビルド プロファイルの結果、たとえばプロジェクトの設定に Gradle で長い時間を要していると判明した場合は、カスタムビルド ロジックを設定フェーズから移行するなどの対処が必要です。また、ビルド時間の大半を mergeDevDebugResources タスクが占めている場合は、画像の WebP 変換PNG 変換の無効化が必要になります。

一般的にプロファイルによってビルドを高速化するには、プロファイルを有効にしてビルドを実施し、ビルド設定を微調整して何度かプロファイルを行い、変更による効果を確認します。

ビルド プロファイルを生成し、表示する手順は次のとおりです。

  1. Android Studio で該当のプロジェクトを開いて [View] > [Tool Windows] > [Terminal] を選択して、プロジェクトのルートでコマンドラインを起動します。
  2. 以下のコマンドでクリーンビルドを実施します。ビルドのプロファイルを行う際は、プロファイル対象のビルドを行う前にクリーンビルドを実施してください。タスクへの入力(ソースコードなど)が変更されていない場合、Gradle ではタスクをスキップするためです。続けてビルドをすると、あとのビルドでは入力の変更がないためタスクが再実行されず、必然的に速度が上がります。よって、確実にフルビルド プロセスでプロファイルを行うためには、ビルドの前に clean タスクを実行してください。
    // On Mac or Linux, run the Gradle wrapper using "./gradlew".
    gradlew clean
    
  3. 次のフラグをつけて、"dev" などの任意のプロダクト フレーバーでデバッグビルドを実行します。
    gradlew --profile --recompile-scripts --offline --rerun-tasks assembleFlavorDebug
    
    • --profile:プロファイルを有効にします。
    • --recompile-scripts:キャッシュをバイパスして、スクリプトを強制的に再コンパイルします。
    • --offline:Gradle によるオンラインでの依存関係の取得を無効にします。これにより、Gradle が依存関係の更新を試みたときに遅延が発生することがなくなるため、プロファイル データにも影響が生じません。Gradle で依存関係のあるファイルをダウンロードおよびキャッシュした状態にするには、プロジェクトを 1 回ビルドしておきます。
    • --rerun-tasks:Gradle ですべてのタスクを再実行し、タスクの最適化は行いません。
  4. ビルドが完了したら、[Project] ウィンドウで project-root/build/reports/profile/ ディレクトリに移動します(図 1 を参照)。

    図 1. プロファイル レポートの場所を示す Project ビュー

  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. タスクの実行結果の表示

これでビルドのプロファイル レポートが取得できたので、レポートの各タブの情報を参考にして最適化できそうな箇所を探すことができます。一部のビルド設定はプロジェクトやワークステーションによって効果が異なるため、さまざまな設定を試す必要があります。たとえば大規模なコードベースを含むプロジェクトは ProGuard を使用して、不要なコードを除去し、APK サイズを縮小すると効果があるでしょう。一方、小規模なプロジェクトでは ProGuard を無効にしたほうが有効だという結果になるかもしれません。また、メモリの少ないマシンでは Gradle のヒープサイズを増やすと逆効果になる可能性があります。

ビルド設定を変更したら、上記の手順を繰り返して新しいビルド プロファイルを生成し、変更の効果を確認します。例として 図 4 に、このページで紹介した基本的な最適化をいくつか施した同一のサンプルアプリのレポートを示します。

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

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