🍔

Androidアプリ開発をマルチモジュール化してみた

2023/10/05に公開

デフォルトだと、appモジュールだけだけど、新しいモジュールを手動で追加できますよっていうお話。

◆メリット

マルチモジュールのメリット詳細については、AndoirDepeloers公式サイトを見ると詳しく載ってるので、そっちを参照してね。
https://developer.android.com/topic/modularization?hl=ja

■TL;DR

公式読むのがしんどい人向けに、自分なりに、中学生が読んでもわかるように、メリットを箇条書きで書き出してみた。(漏れ・ミス等、なにかあれば、コメントで指摘よろしくお願いします)

●恩恵の多い(と思ってる)メリット

  • ビルドスピードが早くなるよ!(並列でビルド処理できるようになるから)
  • 各モジュールごとに、役割機能を分担させて、その役割ごとに実装できると、コード修正するときに「どの機能が、どのモジュールにあるのか」いち早く把握できるようになって、コスト削減できるよ!(認知負荷の軽減)
  • モジュールごとに機能切り出して共有することも可能だぜ!(筆者はやった経験ないけど..。)

●そこそこ使えそうなメリット

  • publicや、privateのような有効範囲の識別子として、新たにinternalが使えるよ!internalは「そのモジュール間のみ参照できるもの」だよ!
  • なんかテストコードでのテストがやりやすくなるよ(やったことあんまりないので詳細は調べて!)

◆デメリット

実はデメリットもあります。

●デメリット考察

  • 実装機能をちゃんと適切なモジュールに追加しないと、実装機能とモジュールの役割との整合性が取れなくなり、魑魅魍魎のスパゲティコードになる恐れがある
  • モジュールを作成しすぎて管理できなくなると、どこにどの機能が実装されているのか、逆にわかりにくくなってしまい、逆に認知不可が高くなる
  • DeepLinkとの相性が悪いらしい(※)

(※)以前、参画してた現場で、「マルチモジュールだとDeeplink実装しづらいから、マルチモジュール化を廃止したい」というお話を耳にしたことあり(具体的なことは把握してない)。そうだとすると、DeeplinkはAndroidアプリ開発において、まぁまぁの頻度で使われるので、そこは公式にがんばって改善してほしい(他力本願)。

◆実装方法

それでは、実装方法を説明しますがー、例によってそんな大層なことではなく、誰でもできます。

■環境

Android Studio Giraffe | 2022.3.1 Patch 1

■手順

  1. Emptyの適当なプロジェクトを作る

  2. どこでもいいから右クリックメニューから、New -> Moduleを選択する

  3. 左サイドメニューから、[library]を選択して、「Module Name」に任意の名前をつけて「Finish」を押す(「Project Name」は「Module Name」に合わせて自動的に変更されるので何もしなくていい)

  4. 自動同期が走るので、終わるまで待機

  5. 同期が終わったあとに、下記のようなアイコンになればOK

◆他モジュールからの参照方法

bruild.gradle.ktsで参照したいモジュールをimplementation(project(":(モジュール名)")で宣言してから同期すれば、他モジュールで定義した関数(publicのみ)が参照できる。

build.gradle.kts(appモジュール)

dependencies {

    implementation(project(":KZTModule"))
    ・・・
    
}

test.kt(KZTModuleモジュール)

fun test() {
    // appから呼べるか確認するためだけの関数
}

KZTModuleのtest関数をappで呼び出す。

MainActivity.kt(appモジュール)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CheckTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting("Android")
                    test() // KZTModuleの関数呼び出し
                }
            }
        }
    }
}

◆小ネタ

■モジュールのリネームはできないと考えたほうがいい

モジュール名をあとから変えたいと思って、「Rename」コマンドからリネームした結果、見かけ上の名前は変更されたが、同期やビルドが通らなくなってしまった。おそらく、参照しているところまで自動的に変更されずに参照できなくなってしまうものと考えられる。

ググっても解決方法らしきもの見当たらないため、名前をつけるときはリネームできないと考えながらつけたほうが良さそう。(なにか、いい感じの解決策があったら、コメントで指摘よろしくお願いします。)

■モジュールの命名基準

迷う...めっちゃ迷う。けど、参考サイト見ると、こうしたら良さそうって指針は立てれる。
https://droidkaigi.github.io/codelab-2020/ja/index.html

  • 役割を示す接頭辞をつけたほうが良さそう
    • feature -> 機能的な役割っぽい。view表示に関わるものが集約されるかな
    • data -> APIのやり取りを集約したものっぽい
    • model -> APIの受け皿のモデルクラスを集約したものっぽい
  • 接頭辞がもつ役割 + 個別の機能をベースに命名すると良さそう
    • ex) feature-login

■モジュールの削除方法

モジュール右クリックからは、削除できないので、知らないと結構消し方がわかりにくい。

●消し方

  1. 削除するモジュールを、bruild.gradle.ktsで宣言している別の各モジュールから削除する

  2. File -> Project Structureを押す

  3. 左のサイドメニューからModuleを選ぶ

  4. 対象モジュールを選択して、マイナスボタンか、右クリックから「Remove Module」を選択する

  5. 自動同期が始まり、同期が終了すると、削除したモジュールが、モジュール定義から外れる(Android Studioのモジュールアイコンではなく、フォルダアイコンに変わる)

  6. 4の状態から右クリックで「Delete」を選択する

◆実装してみての感想

名前を後で変えるといろいろややこしいことになるので、名前の付け方は重要だと実装後に思った。ただ、どんな名前つければいいのか、判断が難しいと思ったので、本記事で整理しておいた。

あと、build.gradle.ktsに宣言するとき、implementation(project(":(モジュール名))"というのが、わかりにくいと思った。「: (コロン)」つけないといけないのが、宣言漏れやすくしている気がする。(なんでモジュール名だけだとダメなのか...?)

まぁこういうのは何事もバランスが大事なので、脳死でマルチモジュール化にすればいいというわけではなく、プロジェクト適正に合うようにマルチモジュール化するか検討していき、新たなモジュールを作るにしても、適量に留めることが肝だと思う。

Discussion