ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Build Variants によって別バージョンの Android アプリを同じプロジェクトからビルドする (Gradle 使用)

前回の記事 に引き続き、Gradle を使った Android アプリのビルドの話。

今回は build variants (build types と product flavors) について調べたので軽くまとめておく。 複数の build variant を定義しておくと、

  • アプリの本体部分のソースコードはほとんど一緒だが、少しだけ違う部分があるアプリを 1 つのプロジェクトでビルドする
  • 社内リリース用のパッケージ名は本番リリース用のパッケージとは別にする

といった用途に便利である。

追記: Android Gradle plugin の新しいバージョンでのプロパティ名の変更

  • ProductFlavor.packageName is now applicationId. Setters for packageName are still temporarily present but will disappear in 1.0
New Build System - Android Tools Project Site

上記リリースノートにあるように、Android Gradle plugin 0.11.0 から、packageNameapplicationId という名前に変更されました。 packageNameSuffixapplicationIdSuffix になりました。 本記事のサンプルコードは修正していませんが、実際にプロジェクトで記述するときには新しいプロパティ名を使ってください。

追記 2 (2015-06-11): 新しいドキュメント

現在では Android Developers のサイトに build variants のドキュメントがあります。 こちらも参考にしてください。

Build Types

デバッグ用のビルド」 と 「リリース用のビルド」 という感じで、複数のビルドの種類を記述することができる。

デフォルトでは、「release」 と 「debug」 という 2 つの種類のビルドが存在する。 自分で新たにビルドの種類を追加することもできる。 また、各ビルドでのパッケージ名に接尾辞を付けるなどといった変更も行うことができる。

    /* build.gradle の例 */
    /* ... */
    android {
        /* ... */

        buildTypes {
            debug {
                /* デバッグ用のパッケージのパッケージ名に接尾辞を付けて、
                   デバッグ用インストール時に本番リリース用のパッケージを
                   上書きしないようにする */
                packageNameSuffix ".debug"
            }

            /* 社内リリース用に新たにビルドの種類を定義する */
            inhouse.initWith(buildTypes.release)
            inhouse {
                packageNameSuffix ".inhouse"
            }
        }
    }

さらに、Java ソースコードやリソースファイルもビルドの種類ごとに別のものを使用できるようにすることも可能なので、例えば debug ビルドと release ビルドでは別のリソースファイルを使用し、表示する文字列を変えたり、アクセスする先の URL を変えたりすることも可能である。

デフォルトでは、src/main 下のファイルと src/buildType*1 のファイルがビルドに使用される。 両方に同じ ID のリソースが定義されている場合は build type ごとの値の方が優先されるっぽい。 また、同じクラス名の Java ソースコードが両方に存在する場合はエラーになるっぽい。

詳細はユーザーガイドへ。

Product Flavors

「広告つきの無料版のアプリ」 と 「広告なしの有料版のアプリ」 といったような、同じアプリの (違いの小さな) 別バージョンを同じプロジェクトからビルドできるようにするための仕組みとして、Product Flavors というものがある。 仕組み的には build types のそれと似ているが、用途が違っている。

例えば、有料版と無料版を別パッケージとして定義するには次のように build.gradle に記述する。

    /* build.gradle の例 */
    /* ... */
    android {
        /* ... */

        productFlavors {
            /* 有料版 */
            pro {
                packageName "info.vividcode.android.app.pfsample.pro"
            }

            /* 無料版 */
            free {
                packageName "info.vividcode.android.app.pfsample.free"
            }
        }
    }

Build type の違いによって使用するリソースファイルや Java ソースコードを変化させられたように、Product flavor の違いでも使用するリソースファイルや Java ソースコードを変化させることができる。

各 flavor の名前に対応するリソースファイルと Java ソースコードは、デフォルトでは src/flavorName に配置されることが期待される。 (試してないが変更も可能だと思われる。)

詳細はユーザーガイドへ。

Build Variants

Build type と production flavor の組を build variants という。 上の例では、

  • pro-debug
  • pro-inhouse
  • pro-release
  • free-debug
  • free-inhouse
  • free-release

の 6 つの build variants が存在することになる。 ビルドのタスクは、build variant ごとに作られる (assembleProductionFlavorBuildType という形式のタスク名)。 例えば pro-debug の build variant をビルドするためには、

gradle assembleProDebug

というコマンドを使うことになる。 上で述べたように、リソースファイルや Java ソースコードは build type 専用だったり production flavor 専用だったりで提供できるわけなので、assembleProDebug のビルドで使用されるリソースファイルや Java ソースコードは次の位置にあるもの全てが使用される *2

  • src/main (常に使用される)
  • src/pro (pro という production flavor 用)
  • src/debug (debug という build type 用)

src/pro や src/debug といったディレクトリが存在しなくてもエラーにはならないので、必要なければ作る必要はない。 ちなみに AndroidManifest.xml ファイルが複数個存在する場合には内容がマージされるようである。

複数の build variants を同時にビルドするタスク

assemblePro や assembleDebug、assemble といったような、複数の build variants を同時にビルドする gradle タスクも存在する。 ここで出した例だと、それぞれ

  • assemblePro : production flavor が pro であるすべての build types のビルド
  • assembleDebug : build type が debug であるすべての production flavors のビルド
  • assemble : すべての build variants のビルド

が行われる。

詳細は

ユーザーガイドへ。

Android Studio では

Android Studio (0.1.6 で確認) も build variants に対応している。 どこから選択するのかめっちゃ迷った *3 のでスクリーンショットを張っておく。

f:id:nobuoka:20130623003438p:plain

build.gradle ファイルを編集して新たに product flavor を追加した場合などは、メニューバーの 「Tools」 から 「Android」 を選び、「Sync Project with Gradle Files」 を実行すればよいようだ。

ビルドに失敗することがある

build.gradle をいじった後の最初のビルドではなぜか NullPointerException が発生してビルドに失敗することがあった。 まだあんまりちゃんと調べてないのでよくわからないが、同僚も 「なんか失敗することがある」 と言っていたのでビルドシステムにまだ穴があったりするのかも。 とはいえ、軽く使ってみた感じではさほど問題はないように感じたので、必要なら普通に使っていって良さそう。

関連記事

*1:debug ビルドなら src/debug、release ビルドなら src/release

*2:明示的に Java ソースコードやリソースファイルの位置を指定していない場合。

*3:気づく人はすぐ気づくだろうけど