BuildVariants毎のパッケージ名差分を自動化する際のTips

# 

環境は android-gradle-plugin 0.8.3 (AndroidStudio 0.4.5)

## やりたかった事

1端末内にBuildVariantのアプリを共存させるために用意するBuildVariant毎のパッケージ名差分をGradleでなんとかウマい事したい


## 気をつける事

- 基本的にパッケージ名を変更するだけでオッケー
- build.gradle の packageName, packageNameSuffix の指定のみでは、よしなにしてくれないところがある
- BuildVariants毎のBuildConfigが作成出来ない
    - BuildType, ProductFlavorそれぞれには `buildConfigField` DSLが提供されているが、BuildVariantsには無い
    - `android.applicationVariants.all` のループでbuildConfigFieldでセットして…という事が出来ない
- AndroidManifestのパーミッションはビルド前に書き換える必要がある
- AndroidManifestのprivider@authoritiesは端末内で一意にする必要がある
    - authoritiesをGroovy等の実機転送前に動的に変更を加える場合、ビルド後かつ実機転送前にやらないといけない


## BuildVariants共存と自動化Tips

共存の基本は以下のリンクを参考に。

- [Build Variants によって別バージョンの Android アプリを同じプロジェクトからビルドする (Gradle 使用) - ひだまりソケットは壊れない](http://vividcode.hatenablog.com/entry/android-app/build-variants)
- [Android - ProductFlavor を宣言した時の、手軽な ContentProvider の Authority の扱い方 - Qiita](http://qiita.com/KeithYokoma/items/0778d568c5814bfa0bf4)
- [ビルドタイプ・プロダクトフレーバーのカスタマイズ可能項目一覧 - sumioの技術メモ](http://sumio.hatenablog.com/entry/2013/07/21/222244)


1端末内に複数のBuildVariantのアプリを共存させるためにはパッケージ名を一意にしなければならない

パッケージ名を一意にするためには　build.gradle で packageName, PackageNameSuffix を指定するだけではうまくいかない事もある

以下に示すようなハマりどころの無いアプリならば、「やりたかった事」のパッケージ名差分の用意の自動化は懸念も特になく出来そうだ


### 共存に必要なもの

build.gradle の packageName, packageNameSuffix の指定のみではダメなところもあるのでそれを自前でなんとかする必要がある

自身のパッケージ名を含む以下の属性を `BuildVariants` 毎のパッケージ名に置き換える

- `<permission>` `<uses-permission>` の name属性
- `<provider>` の authorities属性

### パーミッションの name属性 の置き換え

パーミッションの中には自身のパッケージ名を含むものもある。それをBuildVariant毎のパッケージ名に置き換える必要がある

gradleを利用すると「ビルド後且つapk転送前」に /build/manifest 内にある AndroidManifestを書き換えるようなスクリプトも記述可能

しかし、パーミッションのパッケージ名の書き換えは **ビルド前にやらなければダメ**

ビルド後にAndroidManifestを書き換えて実機に転送しても、パーミッションの定義が無いとエラーを吐いてくラッシュする

``` xml

    <!--  自身のパッケージ名を含む permission@android:name は **ビルド前に** 置き換えないとダメ -->
    <permission
        android:name="com.example.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.example.permission.C2D_MESSAGE" />

```

### privider@authoritiesを動的に用意するフェイズに関する注意点

ContentPrividerを利用する場合はAndroidManifestにauthoritiesを指定する必要があり、1端末内(アプリ内ではなく)で一意でなければならない。

詳しくは下記を参照

- [Android - ProductFlavor を宣言した時の、手軽な ContentProvider の Authority の扱い方 - Qiita](http://qiita.com/KeithYokoma/items/0778d568c5814bfa0bf4)

詳しくは[Google Groupのスレッド](https://groups.google.com/forum/#!searchin/adt-dev/authorities/adt-dev/K29pY0H9vsY/D5Mpn8BMebEJ)を参照

### prividerのauthoritiesを書き換える


この記事ではBuildConfigを利用してProductFlavor毎のAuthoritiesを用意しているが、「気をつける事」で示したように、`buildConfigField` では BuildVariants 毎のBuildCOnfigは用意出来ないため、 BuildTtypes x ProductFlavors の分だけパッケージ差分があるアプリについては上記の記事とは別のやり方を考える必要がある

``` groovy
// 例) パッケージ名の共通部分をBuildConfigに定義して置換してみる

android {

    defaultConfig {
        buildConfigField 'String', 'BASE_PACKAGE_NAME', '\"com.example\"'
    }

    buildTypes {
        release {
        }
        
        debug {
            packageNameSuffix '.debug'
        }
    }

    productFlavors {
        dev {
            packageName 'com.example.dev'
        }

        prod {
            packageName 'com.example'
        }
    }
}

```

``` java
package com.example.MyProvider;

class MyProvider extends ContentProvider {

    public String getAuthorities() {
        return MyProvider.class.getName().replace(BuildConfig.BASE_PACKAGE_NAME, BuildConfig.PACKAGE_NAME);
    }
}

```

## 結局どうするのがいいのか

パッケージ名の差分くらいなら簡単に自動化出来るだろう、という軽い気持ちで始めてみたら、案外すんなりいかなくて四苦八苦。

(少なくとも自分の中では) 汎用的なパターンとして固まっておらず、gradleプラグインのサポートも不十分であるため、パッケージ名の差分程度とはいえ、上記を踏まえた上での多少アナログな手法が混じる程度がよさそう

ここでまとめた事が混乱の元にならないように、ヘタにGradleを使わずに手動でファイルをコピーしてBuildVariants毎のAndroidManifestを用意する方法が一番平和的かもしれない

もし現状でなんとか自動化したいのなら、gradleプラグインだけで頑張るよりも、生のBuildConfigや build/manifest/ を Perl, Ruby あたりにテキスト処理を任せて置換を噛ますのもアリかもしれない

BuildVariantsの共存は単にアプリに内包する実装を切り替えるだけではなくて、リリースビルドとデバッグビルドで共存出来ると普段の開発でもアプリの(再)インストールの機会が減って楽になるから、どんなアプリでもぜひとも標準で取り入れたい機構。

将来的にはこのパッケージ差分は全部プラグインが吸収してくれたらいいね