Androidアプリのビルドを高速化するDex In Processが有効になる条件

# きっかけ
Androidの実行ファイルにコンパイルするDexをプロセス内で行うことにより、Androidアプリのビルドを高速化するDexInProcessですが、いつ有効になったりならなかったりするのかがよくわかりません。(詳しい仕組みはわかっていません)
詳しい判定をちょっとソース見たら分かりそうじゃないかなと思って見てみました。

# まとめ

* Android Gradle Pluginを2.1.2以上を使っている(この仕様になったのがどのバージョンからなのかは調べられていません) 設定場所:プロジェクト/build.gradleで設定
* Build Tools Versionが23.0.2以上 設定場所:プロジェクト/app/build.gradleで設定
* 自分でdexInProcess falseにしていない

`./gradlew clean asseDeb`などでビルドして以下のような**警告文が出なければ有効になっている**

```
Running dex as a separate process.

To run dex in process, the Gradle daemon needs a larger heap.
It currently has 520 MB.
For faster builds, increase the maximum heap size for the Gradle daemon to at least 1536 MB.
To do this set org.gradle.jvmargs=-Xmx1536M in the project gradle.properties.
For more information see https://docs.gradle.org/current/userguide/build_environment.html

```

また警告文が出た場合は、この`To do this set org.gradle.jvmargs=-Xmx1536M in the project gradle.properties.`に従って、

プロジェクト/gradle.propertiesに

```
org.gradle.jvmargs=-Xmx1536M
```

を書けばOK(**状況によって違うようなので1536Mの部分はコピペしないでください**)


ただAndroid Gradle Plugin 2.1.2だと計算にバグがあり、javaMaxHeapSizeを設定していると、gradle.propertiesに記述しても有効にならない場合があるようです。Android Gradle Plugin 2.2で修正されているみたいです。
https://code.google.com/p/android/issues/detail?id=208214

# 読んでみた
ソースコードを読んでみたらshouldDexInProcessというメソッドで判定しているようです。
https://android.googlesource.com/platform/tools/base/+/028ba07/build-system/builder/src/main/java/com/android/builder/core/DexByteCodeConverter.java#250

```java
    /**
     * Determine whether to dex in process.
     */
    @VisibleForTesting
    synchronized boolean shouldDexInProcess(
            @NonNull DexOptions dexOptions,
            @NonNull Revision buildToolsVersion) {
```

自分でgradleでdexInProcessを無効に設定していたらそっちを優先する(デフォルトは有効)

```java
        if (!dexOptions.getDexInProcess()) {
            mIsDexInProcess = false;
            return false;
        }
```

Build Tools Versionが23.0.2未満だったら警告文出して無効にする

```java
        if (buildToolsVersion.compareTo(DexProcessBuilder.FIXED_DX_MERGER) < 0) {
            // We substitute Dex > 23.0.2 with the local implementation.
            mLogger.warning("Running dex in-process requires build tools %1$s.\n"
                            + "For faster builds update this project to use the latest build tools.",
                    DexProcessBuilder.FIXED_DX_MERGER.toShortString());
            mIsDexInProcess = false;
            return false;
        }

```

ちなみにFIXED_DX_MERGERは以下のようになっている

```java
ublic static final Revision FIXED_DX_MERGER = new Revision(23, 0, 2);
```

あとは自分が動いているVMの最大ヒープサイズ(gradle.propertiesのorg.gradle.jvmargsで設定)と Gradleで以下のように宣言できるjavaMaxHeapSizeを比較して、javaMaxHeapSize以上でないと警告文を出す感じです。
自分のプロセス内でビルドするので、その時にこのヒープサイズが必要になるということだと思いました。
ちなみに`javaMaxHeapSize '4g'`しているとgradle.propertiesに`org.gradle.jvmargs=-Xmx4608M`を記述するように言われました。

```groovy
    dexOptions {
        javaMaxHeapSize '4g'
    }

```

```java
        // Requested memory for dex.
        long requestedHeapSize = parseHeapSize(dexOptions.getJavaMaxHeapSize(), mLogger);
        // Approximate heap size requested.
        long requiredHeapSizeHeuristic = requestedHeapSize + NON_DEX_HEAP_SIZE;
        // Get the approximate heap size that was specified by the user.
        // It is important that this be close to the -Xmx value specified, as is is compared with
        // the requiredHeapSizeHeuristic, which we suggest the user sets in their gradle.properties.
        long maxMemory = 0;
        for (MemoryPoolMXBean mpBean: ManagementFactory.getMemoryPoolMXBeans()) {
            if (mpBean.getType() == MemoryType.HEAP) {
                maxMemory += mpBean.getUsage().getMax();
            }
        }
        // Allow a little extra overhead (50M) as in practice the sum of the heap pools is
        // slightly lower than the Xmx setting specified by the user.
        final long EXTRA_HEAP_OVERHEAD =  50 * 1024 * 1024;
        if (requiredHeapSizeHeuristic > maxMemory + EXTRA_HEAP_OVERHEAD) {
            String dexOptionsComment = "";
            if (dexOptions.getJavaMaxHeapSize() != null) {
                dexOptionsComment = String.format(
                        " (based on the dexOptions.javaMaxHeapSize = %s)",
                        dexOptions.getJavaMaxHeapSize());
            }
            mLogger.warning("\nRunning dex as a separate process.\n\n"
                            + "To run dex in process, the Gradle daemon needs a larger heap.\n"
                            + "It currently has approximately %1$d MB.\n"
                            + "For faster builds, increase the maximum heap size for the "
                            + "Gradle daemon to more than %2$s MB%3$s.\n"
                            + "To do this set org.gradle.jvmargs=-Xmx%2$sM in the "
                            + "project gradle.properties.\n"
                            + "For more information see "
                            + "https://docs.gradle.org/current/userguide/build_environment.html\n",
                    maxMemory / (1024 * 1024),
                    requiredHeapSizeHeuristic / (1024 * 1024),
                    dexOptionsComment);
            mIsDexInProcess = false;
            return false;
        }
        mIsDexInProcess = true;
        return true;

```