Infinito Nirone 7

白羽の矢を刺すスタイル

BuildType と ProductFlavor のマトリクスで BuildConfigField や ManifestPlaceholders を生成したい

BuildConfigField は、BuildConfig という自動生成されるクラスに定義する定数をビルドスクリプトから制御する仕組みです。 そしてもうひとつ、ManifestPlaceholders は AndroidManifest のプレースホルダーに放り込む値をビルドスクリプトから制御する仕組みです。

これらを活用すると、BuildVariants を変えただけで気軽にアプリの振る舞いを変えたり、ContentProvider の Authority の衝突を防いで異なる BuildVariants の apk を同じ端末内に共存させたりといったことができるようになります。

ContentProvider の衝突を防ぐという目的では、次のように debug と release のそれぞれの BuildType で別々の Authority を定義するのが一般的です。

android {
  buildTypes {
    debug {
      manifestPlaceholders = [
        authorityHogehogeProvider : "com.sample.hogehoge.debug.provider.HogehogeProvider"
      ]
      BuildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.debug.provider.HogehogeProvider\""
    }
    release {
      manifestPlaceholders = [
        authorityHogehogeProvider : "com.sample.hogehoge.provider.HogehogeProvider"
      ]
      buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.provider.HogehogeProvider\""
    }
  }
}

こうしておけば、AndroidManifest.xmlは main ソースセット配下の一個だけで済みます。

<manifest>
  <application>
    <provider
      android:name=".provider.HogehogeProvider"
      android:authorities="${authorityHogehogeProvider}"
      android:label="HogehogeProvider"
      android:exported="false"/>
  </application>
</manifest>

ここで、この ContentProvider を flavor1 と flavor2 という2つの ProductFlavor でそれぞれ持つことを考えます。 先程の buildTypes で manifestPlaceholders を分けるコードのみでは、flavor1 と flavor2 とで ContentProvider が衝突するため共存できません。 ProductFlavor と BuildType の組み合わせで manifestPlaceholders や BuildConfigField を制御する場合は、次のようにすべての BuildVariants を走査して対応するものを差し込むロジックを書きます。

android {
  productFlavors {
    flavor1 {}
    flavor2 {}
  }

  buildTypes {
    debug {}
    release {}
  }

  applicationVariants.all { variant ->
    def flavorName = variant.productFlavors.get(0).name
    def typeName = variant.buildType.name

    if (flavorName.equals("flavor1")) {
      if (typeName.equals("debug") {
        // BuildVariant: flavor1Debug
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor1.debug.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor1.debug.provider.HogehogeProvider\""
      } else if (typeName.equals("release") {
        // BuildVariant: flavor1Release
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor1.release.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor1.release.provider.HogehogeProvider\""
      }
    } else if (flavorName.equals("flavor2") {
      if (typeName.equals("debug") {
        // BuildVariant: flavor2Debug
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor2.debug.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor2.debug.provider.HogehogeProvider\""
      } else if (typeName.equals("release") {
        // BuildVariant: flavor2Release
        variant.mergedFlavor.manifestPlaceholders = [
          authorityHogehogeProvider : "com.sample.hogehoge.flavor2.release.provider.HogehogeProvider"
        ]
        variant.buildConfigField "String", "AUTHORITY_HOGEHOGE_PROVIDER", "\"com.sample.hogehoge.flavor2.release.provider.HogehogeProvider\""
      }
    }
  }
}

ProductFlavor は複数定義可能なので、複数ある場合はvariant.productFlavors.get(n)nも適宜どの ProductFlavor に対して BuildConfigField や ManifestPlaceholders を差し込むかによって変えます。 BuildConfigField は variant が直接持っていますが、ManifestPlaceholders は variant.mergedFlavor 配下に対して設定します。variant.buildTypeにもmanifestPlaceholdersがありますが、こちらには設定できないことに注意しましょう。