【Android】AdMobのアプリIDや広告ユニットIDをコミットしないで管理する方法

# はじめに

みなさんは、AdMobで発行されたアプリIDや広告ユニットのIDの管理ついてどのように行なっていますか？
実際に、テスト用のIDではなく、本番用のIDを発行した際にどのように管理すれば良いのか戸惑いました。

そこで、今回はbuild.gradleを使ってビルドタイプごとに使用するAdMobのIDを切り分けて、コード内へのベタ書きを防ぐ方法を解説します。
ここで紹介する手順に沿って開発を行えば、AdMobで発行される広告用のIDもGitHub等へのコミットを回避して開発を行うことができるようになります。

# admob.propertiesを作成する

**admob.properties**を、**app**や**gradle**などのディレクトリが並ぶルートディレクトリに作成します。

```properties
DEBUG_ADMOB_APP_ID="ca-app-pub-3940256099942544~3347511713"
DEBUG_ADMOB_BOTTOM_BANNER_AD_UNIT_ID="ca-app-pub-3940256099942544/6300978111"
RELEASE_ADMOB_APP_ID="ca-app-pub-xxx~xxx"
RELEASE_ADMOB_BOTTOM_BANNER_AD_UNIT_ID="ca-app-pub-xxx/xxx"
```

このとき、４種類の変数を定義します。

- DEBUG_ADMOB_APP_ID
    - テスト用のアプリID
- DEBUG_ADMOB_BOTTOM_BANNER_AD_UNIT_ID
    - テスト用の広告ユニットID
- RELEASE_ADMOB_APP_ID
    - 本番用のアプリID
- RELEASE_ADMOB_BOTTOM_BANNER_AD_UNIT_ID
    - 本番用の広告ユニットID

ファイルの設定が終了したら、ルートディレクトリの**.gitignore**に**admob.properties**を追加しておきます。

```.gitignore
...
admob.properties
```

# buildConfigField()を使ってKotlinファイルで参照できるようにする

最初に、先ほど設定した**admob.properties**を**build.gradle**にて読み込みます。

```gradle
def admobPropertiesFile = rootProject.file("admob.properties")
def admobProperties = new Properties()
admobProperties.load(new FileInputStream(admobPropertiesFile))
android {
    ...
}
```

そして、読み込んだ値を使用して各BuildTypeごとにBuildConfigの内容を設定していきます。

```gradle
android {
    ...
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.debug

            // AdMob
            buildConfigField("String", "ADMOB_APP_ID", admobProperties["RELEASE_ADMOB_APP_ID"])
            buildConfigField("String", "ADMOB_BOTTOM_BANNER_AD_UNIT_ID", admobProperties["RELEASE_ADMOB_BOTTOM_BANNER_AD_UNIT_ID"])
            ...
        }
        debug {
            applicationIdSuffix ".debug"
            debuggable true

            // AdMob
            buildConfigField("String", "ADMOB_APP_ID", admobProperties["DEBUG_ADMOB_APP_ID"])
            buildConfigField("String", "ADMOB_BOTTOM_BANNER_AD_UNIT_ID", admobProperties["DEBUG_ADMOB_BOTTOM_BANNER_AD_UNIT_ID"])
            ...
        }
    }
}
```

この設定により、Kotlinファイル内で`BuildConfig`を通して`ADMOB_APP_ID`などにアクセスすることができるようになります。

```kotlin
AndroidView(
    modifier = Modifier.fillMaxWidth(),
    factory = { context ->
        val adView = AdView(context).apply {
            setAdSize(AdSize.BANNER)
            adUnitId = BuildConfig.ADMOB_BOTTOM_BANNER_AD_UNIT_ID
            loadAd(AdRequest.Builder().build())
        }
        adView.rootView
    }
)
```

# AndroidManifext.xmlでAdMobのアプリIDを参照できるようにする

先ほど`buildConfigField()`を呼び出した箇所で、`manifestPlaceholders`に対する設定を追加します。

```gradle
def admobPropertiesFile = rootProject.file("admob.properties")
def admobProperties = new Properties()
admobProperties.load(new FileInputStream(admobPropertiesFile))

android {
    buildTypes {
        release {
            ...
            manifestPlaceholders = [
                    ADMOB_APP_ID: admobProperties["RELEASE_ADMOB_APP_ID"].replace("\"", "")
            ]
        }
        debug {
            ...
            manifestPlaceholders = [
                    ADMOB_APP_ID: admobProperties["DEBUG_ADMOB_APP_ID"].replace("\"", "")
            ]
        }
    }
}
```

この設定により、AndroidManifest.xmlにおいて`ADMOB_APP_ID`を参照できるようになりました。

```xml
<application>
    ...
    <meta-data
        android:name="com.google.android.gms.ads.APPLICATION_ID"
        android:value="${ADMOB_APP_ID}"
        />
</application>
```

## replace()を呼び出す理由

admob.propertiesで、`DEBUG_ADMOB_APP_ID`を設定したときにダブルクオーテーションで囲みました。

```properties
DEBUG_ADMOB_APP_ID="ca-app-pub-3940256099942544~3347511713"
...
```

そのため、この値をそのまま`manifestPlaceholders`に渡すと、AndroidManifest.xmlの内容がマージされたときに次のようになってしまいます。

```xml
<meta-data
    android:name="com.google.android.gms.ads.APPLICATION_ID"
    android:value = ""ca-app-pub-3940256099942544~3347511713""
    />
```

https://stackoverflow.com/questions/32087870/manifest-merger-it-is-possible-to-see-the-resulting-mixed-manifest

その結果、次のようなエラーが出力されてアプリはクラッシュしてしまいます。

```shell
******************************************************************************
* Invalid application ID. Follow instructions here:                          *
* https://googlemobileadssdk.page.link/admob-android-update-manifest         *
* to find your app ID.                                                       *
* Google Ad Manager publishers should follow instructions here:              *
* https://googlemobileadssdk.page.link/ad-manager-android-update-manifest.   *
******************************************************************************
```

これを防ぐために、`replace("\"", "")`を呼び出して先頭と末尾の`"`を取り除いた状態で渡しています。

# buildConfigの機能を有効にする

**build.gradle**にて、何も設定しない状態で`buildConfigField()`を呼び出した場合、次のようなエラーが発生する可能性があります。

```shell
Build Type 'debug' contains custom BuildConfig fields, but the feature is disabled.
Affected Modules: app
```

Build Typeが`release`の場合には次のようなエラーが表示されます。

```shell
Build Type 'release' contains custom BuildConfig fields, but the feature is disabled.
Affected Modules: app
```

これを解決するためには、**build.gradle**にて`buildFeatures`の設定を追加しましょう。このとき、`buildConfig = true`を設定します。

```gradle
android {
    ...
    buildFeatures {
        buildConfig = true
    }
    buildTypes {
        ...
    }
}
```

これにより、**build.gradle**を同期しても先ほどのようなエラーは表示されなくなったと思います。

https://stackoverflow.com/questions/74634321/fixing-the-build-type-contains-custom-buildconfig-fields-but-the-feature-is-di

# BuildConfigが見つからない

**build.gradle**を同期しただけでは、アプリケーションファイル内で`BuildConfig`を参照できない場合があります。

Android Studioで次の手順を実行します。

1. **Build**をクリックして、**Clean Project**を実行する
2. **File**をクリックして、**Invalidate Caches...**をクリックして、**Invalidate and Restart**を実行する
3. **Build**をクリックして、**Rebuild Project** を実行する

https://stackoverflow.com/a/64043197

僕の場合は、３番目の**Rebuild Project**の実行により、`BuildConfig`の参照が解決しました。
つまり、`com.projectname.appname.BuildConfig`が生成されるということですので、これをインポートしましょう。

```kotlin
import com.projectname.appname.BuildConfig
```

# まとめ

あまりbuild.gradleには、依存関係の追加時以外触れてこなかったので、ビルドタイプごとに同じで変数で違う値を`BuildConfig`を通して公開できるということに非常に感動しました。

やはり、API Keyなども含めてリポジトリへのコミットはできる限り避けたいので、今後も**build.gradle**を使いながら工夫してより良い開発体験を目指していきたいです。

# 参考にした記事

https://blog.integrityworks.co.jp/2020/02/17/kotlin-applicationi-d-key-no-version-management-system/

https://guides.codepath.com/android/Storing-Secret-Keys-in-Android

https://developer.android.com/studio/build/gradle-tips?hl=ja

https://developer.android.com/studio/build/manifest-build-variables?hl=ja#groovy