LoginSignup
6
0

More than 1 year has passed since last update.

Product Flavor を複数のアプリモジュールに展開したお話

Last updated at Posted at 2021-12-20

はじめに

Android用のTwitterクライアントやmixiビューアといった個人開発アプリ1をいくつか開発しています。

Twitterアプリの TwitPane は 無料版有料版Kindle版 という3種類を同一ソースから生成しています。

2013年頃から開発しているので Eclipse+ADT 時代にどうやっていたのかはもう既に覚えていませんが、Android Studio に移行して Product Flavor(以下 Flavor) で気軽に作り分けできるようになったときはずいぶん感動した記憶があります。

それから7年ほど経ち、ソースコードが巨大化してマルチモジュール構成になり、Flavor の切替だけで10秒くらいかかるようになり、(クラス名・メソッド名の変更といった)Android Studio のリファクタリング機能が「現在の Flavor にしか適用されない」といったつらみがきつくなってきていると薄々感じていた頃、Flavor はもうすっかり時代遅れであることに気づかされました。

「アプリケーションモジュールを量産」かぁ、なるほど🤔(どうやるんだろう、さっぱり分からねえ)

というわけでこの記事は Product Flavor を複数のアプリケーションモジュールに分離・展開したお話です。

そもそも Product Flavor とは

Android Studio (Android Gradle Plugin) には Product FlavorBuild Type という機能があり、テスト用、本番用などでアプリのビルド設定・ソースコードを切り替えることができます。

Build Type はおなじみの debugrelease という分類で、Flavorproductiondevelop といった分類で使うことが多いようです。

この FlavorBuild Type を組み合わせたものが Build Variant で、Android Studio の Build Variants パネルから変更することができます。

うちのアプリの場合は

  • Flavor : free, kindle, premium
  • Build Type : debug, release

なので、

  • Build Variants : freeDebug, freeRelease, kindleDebug, kindleRelease, premiumDebug, premiumRelease

の計6種類あります。

(ということで当時のソースを開いてみるとさらに(わけあって) dimension も使っていましたが割愛します)

android {
    flavorDimensions("tier", "edition")
    productFlavors {
        // tier
        tp1 {
            dimension "tier"
            isDefault = true
        }
        tp2 {
            dimension "tier"
        }

        // edition
        free {
            dimension "edition"
            applicationId "com.twitpane"
            manifestPlaceholders = [oauthCallbackScheme: "twitpane"]
            isDefault = true
        }
        kindle {
            dimension "edition"
            applicationId "com.twitpane.kindle"
            manifestPlaceholders = [oauthCallbackScheme: "twitpanekindle"]
        }
        premium {
            dimension "edition"
            applicationId "com.twitpane.premium"
            manifestPlaceholders = [oauthCallbackScheme: "twitpanepremium"]
        }
    }

Build Variants パネルはこんな感じ↓

image.png

複数のアプリケーションモジュールとは?

モジュールには主に下記があります。

  • Application Module
  • Library Module
  • Dynamic Feature Module

個人的になんとなく、1プロジェクト内には Application Module は1つだけ存在できるんだろうと思っていましたが、全然そんなことなかったんですね。

端的に言えば

build.gradle
apply plugin: "com.android.application"

を指定しているモジュール(たいてい app という名前でしょう)をコピペして、app_freeapp_kindle といった名前にして、settings.gradle から参照してあげれば完成です。なんだ、簡単じゃん?

settings.gradle
...
include ':app_free'
include ":app_kindle"
include ":app_premium"

Android Studio の Configuration パネルもこんな感じになりました↓

image.png

とはいえ、、ソースコード・リソース同士が有機的に結合しているので(それ故に Flavor を使っているので)、そう簡単に複数アプリに展開できるわけがありません。

マルチモジュール展開と同様の苦しみがありました。

作業ログ

ここからは大雑把に作業中のツイートから抜粋していきます。

マルチアプリケーション化に手を付ける前のつぶやきから。

実際にその手順でやっていきました。

config_impl モジュールの分離

config_impl モジュールの各Flavorに存在しているクラスについて、大まかなロジックと参照元を表にまとめ、config_impl_free, config_impl_kinle, config_impl_premium モジュールに移動していき、DI で app の各Flavorでinjectするものを切り替える、といった形で実装していきました。

main モジュールの分離

次は main モジュールを main_free, main_kindle, main_premium に分離していく。

図にするとこんな感じ。

image.png

app モジュールの分離

いよいよ本丸の app モジュールの分離。

やりたいことは下記の図のような感じで、app モジュール内の main ディレクトリ配下を app_common モジュールに抽出し、各Flavor毎に app_free, app_kindle, app_premium に分離する流れ。

image.png

ところで

参考URL


  1. 法人化していますが実質開発メンバー1人の個人開発です。 

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0