ProductFlavor による用途別の apk の生成
ProductFlavor とは
Gradle で ProductFlavor を使うと、用途別に apk を作り分けることができます。
例えば、バックエンドを開発用のものと本番用のものとを用意して適宜切り替えたり、課金版と無料版とを切り替えたり、といった具合に、apk を作り分けることができます。
apk を作り分けるにあたって、パッケージ名を ProductFlavor 毎に定義することもでき、これによって、用途別に作り分けた apk を一つの端末上に共存させることが出来るようになります。
ContentProvider を持つアプリでの ProductFlavor
さて、この時、ContentProvider を持つアプリを作ることを考えると、同じ端末上に同じ Authority を持つ ContentProvider を共存させることはできない為、通常とは別の手順を踏んで、ContentProvider の Authority を ProductFlavor 毎に変える作業が必要になります。
この記事では、その手順をまとめます。
作業全体の概要
やることは以下のとおりです。
- build.gradle の中の productFlavors の宣言で、BuildConfig に宣言するフィールドを追記する
- AndroidManifest.xml を、productFlavors 毎に用意し、
<provider>
のandroid:authorities
を、productFlavors 毎 BuildConfig に宣言したものにする - ContentProvider の実装で AUTHORITY を BuildConfig に宣言したものを参照するようにする
- 通常通り ContentProvider の中身を実装する
また、前提として、AndroidStudio を最新版(0.4.3)にしておくこと、Gradle Plugin のバージョンを最新(0.8.+)にしておくこと、Gradle 自体のバージョンを(1.10)にしておくことをご確認下さい。
build.gradle の準備
ビルドスクリプトには、ProductFlavor ごとに BuildConfig に宣言するフィールドの値を変えるようにしておきます。
最新の Gradle Plugin(0.8.+) では、BuildConfig にフィールドを宣言するための専用のメソッドが用意されているので、これを使います。
buildConfigField "String", "FIELD_NAME", "\\"field_value\\""
buildConfigField
メソッドに、3 つの引数を渡します。
最初の引数は、フィールドの型です。
次の引数は、フィールドの名前です。
最後の引数は、フィールドに代入する値です。型が String
の場合、文字列リテラルのためのダブルクオートを含める必要が有るため、エスケープシーケンスを使う必要がある点に注意します。
android {
// ... {snip} ...
productFlavors {
staging {
proguardFile 'proguard-rules.txt'
packageName "jp.yokomark.sample.gradle.provider.stg"
buildConfigField "String", "PROVIDER_SAMPLE_AUTHORITY", "\"jp.yokomark.sample.gradle.provider.stg.SampleProvider\""
}
production {
proguardFile 'proguard-rules.txt'
packageName "jp.yokomark.sample.gradle.provider"
buildConfigField "String", "PROVIDER_SAMPLE_AUTHORITY", "\"jp.yokomark.sample.gradle.provider.SampleProvider\""
}
}
}
ProductFlavor 毎のディレクトリに AndroidManifest.xml を格納する
以下に、それぞれのディレクトリに含める AndroidManifest.xml のサンプルを示します。
注意する点として、main/AndroidManifest.xml
には、ContentProvider の宣言を入れないようにしておく必要があります。
production/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.yokomark.sample.gradle.provider" >
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<provider
android:authorities="jp.yokomark.sample.gradle.provider.SampleProvider"
android:name=".SampleProvider"
android:exported="false"/>
</application>
</manifest>
staging/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.yokomark.sample.gradle.provider" >
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<provider
android:authorities="jp.yokomark.sample.gradle.provider.stg.SampleProvider"
android:name=".SampleProvider"
android:exported="false"/>
</application>
</manifest>
ContentProvider の実装で見る AUTHORITY の参照先を BuildConfig に向ける
以上の手順を実行し、一度 Sync Project with Gradle Files を実行しておくと、BuildConfig に build.gradle で宣言したフィールドが注入されているはずです。
その後、以下のように AUHORITY の宣言を BuildConfig のものに切り替えることで、自動で ProductFlavor 毎のものに設定することが出来るようになります。
public class SampleProvider extends ContentProvider {
public static final String AUTHORITY = BuildConfig.PROVIDER_SAMPLE_AUTHORITY;
// ... {snip} ...
}
問題点
AndroidManifest.xml と build.gradle の双方に、Authority の文字列を宣言しなければならないため、間違うととても悲しいことになりますので注意して下さい。
サンプル
GitHub にサンプルのプロジェクト全体のコードを挙げましたので、御覧ください。