メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

ビルド時にいろんな値を生成する

ビルドに応じて値を動的に制御したい欲はあると思います。いくつか方法をまとめました。

BuildConfigに追加する

build.gradleに定義することで、自動生成されるBuildConfig.javaに値を定義できます。

build.gradleに、直接値を書くのではなくて、gradle.propertiesから読み込む方法をとります。

gradle.propertiesの扱いについては、過去の記事を参考にしていただけると。

sakebook.hatenablog.com

gradle.propertiesに適当な値を設定します。

...
from_properties=true
hoge_api_key=hogehogehoge
hoge_version=10000
hoge_array={"a", "b", "c"}
hoge_pi=3.14

properties_margin=48dp
properties_color=#dddddd
hoge_host=http://google.com
hoge_meta_key=hogekey
hoge_meta_id=100
hoge_meta_value=hogevalue
...

build.gradleから次のように取り込みます。 定義した値は、${HOGE}build.gradle内で呼ぶことができます。

...
android {
    ...
    defaultConfig {
        ...
        buildConfigField("boolean", "FROM_PROPERTIES", "${from_properties}")
        buildConfigField("String", "HOGE_API_KEY", "\"${hoge_api_key}\"")
        buildConfigField "int[]", "HOGE_ARRAY", "{1, 2, 3}"
        buildConfigField "String[]", "PROPERTIES_ARRAY", "${hoge_array}"
        buildConfigField("int", "HOGE_VERSION", "${hoge_version}")
        buildConfigField("double", "HOGE_PI", "${hoge_pi}")
        buildConfigField("java.util.ArrayList<String>", "HUGA_LIST", "new java.util.ArrayList<>()")
        ...
    }
    ...
}
...

生成されるBuildConfig.javaは次のようになります。

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.sakebook.sample.gradlepropertiessample";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "log";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
  // Fields from default config.
  public static final boolean FROM_PROPERTIES = true;
  public static final String HOGE_API_KEY = "hogehogehoge";
  public static final int[] HOGE_ARRAY = {1, 2, 3};
  public static final double HOGE_PI = 3.14;
  public static final int HOGE_VERSION = 10000;
  public static final java.util.ArrayList<String> HUGA_LIST = new java.util.ArrayList<>();
  public static final String[] PROPERTIES_ARRAY = {"a", "b", "c"};
}

定義したものが そのまま 呼び出されます。 そのため、フルパスで定義すればArrayListなども生成できます。用途はわかりませんが。。

resourceに追加する

build.gradleからResourceも追加できます。 resValueで定義します。

...
android {
    ...
    defaultConfig {
        ...
        resValue "dimen", "properties_dimen", "${properties_margin}"
        resValue "color", "properties_color", "${properties_color}"
        resValue "string", "hoge_host", "${hoge_host}"
        resValue "string", "hoge_meta_id", "${hoge_meta_id}"
        resValue "string", "hoge_meta_key", "${hoge_meta_key}"
        ...
    }
    ...
}
...

第一引数は、リソースの種類、第二引数はなまえ、第三引数が値となります。 String ではなく string なので注意が必要です。 生成される値は次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Automatically generated file. DO NOT MODIFY -->

    <!-- Values from product flavor: log -->
    <string name="hoge_id">log</string>
    <!-- Values from default config. -->
    <string name="hoge_host">http://google.com</string>
    <string name="hoge_meta_id">100</string>
    <string name="hoge_meta_key">hogekey</string>

    <color name="properties_color">#dddddd</color>

    <dimen name="properties_dimen">48dp</dimen>

</resources>

<resource>タグの中で定義するものならなんでもできそうです。 既に存在する名前を使ってしまうとエラーになります。

Error:Execution failed for task ':app:mergeLogDebugResources'.
> [string/app_name] 省略/app/src/main/res/values/strings.xml   [string/app_name] 省略/app/build/generated/res/resValues/log/debug/values/generated.xml: Error: Duplicate resources

AndroidManifest.xmlを置き換える

生成するだけではなく、置き換えることも可能です。AndroidManifest.xml内で、${HOGE}と書くことで、ビルド時に置き換えることが可能です。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sakebook.sample.gradlepropertiessample" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="${base_app_name}"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="hoge_key"
            android:value="@string/hoge_meta_key" />

        <meta-data
            android:name="hoge_id"
            android:value="@string/hoge_meta_id" />

        <meta-data
            android:name="hoge_value"
            android:value="${meta_value}" />
    </application>

</manifest>

${base_app_name}${meta_value}というplaceholderを置きました。

これらを何に置き換えるか、build.gradleで定義します。

...
android {
    ...
    defaultConfig {
        ...
            manifestPlaceholders = [meta_value: "@string/hoge_id"]
            manifestPlaceholders = [base_app_name: "1name"]
        ...
    }
    ...
}
...

manifestplaceholder = [Manifestに書いたPlaceholder名: 変えたい値]という風に定義します。複数まとめることも可能です。

manifestPlaceholders =
        [
                meta_value: "${hoge_meta_value}",
                base_app_name: "2name"
        ]

applicationIdも、実は デフォルトで用意されているplaceholder です。それ以外は、manifestplaceholderと明示して定義する必要があります。

定義できる場所

これらのbuildConfigField, resValue, manifestplaceholderは、build.gradleandroidDSLの中で、

  • defaultConfig
  • productFlavorsの各フレーバー内
  • buildTypesの各ビルドタイプ内

で定義できます。

そのビルドにおいて、どこかしらで定義さえしていれば大丈夫です。 しかし、同じものを定義していれば defaultConfig < productFlavors < buildTypes の順で上書きされます。

まとめ

ビルド時にそもそもの値を切り替えておけば、APIのホストやGAのIDなどを、BuildConfig.DEBUGで制御したりする必要がなくなります。 AndroidManifestのmeta-dataも変更できるので、各種SDKの切り替えなどにも使えると思います。

荒いですがサンプルをおいておきます。

github.com

参考

Gradleを使って業務を楽にする / Yahoo! JAPAN Tech Blog

Manifest Merger / Android Tools Project Site