fkm blog

software開発に関することを書いていきます

Google I/O 2019: Build a Modular Android App Architecture

youtu.be

動画の終盤でTL;DRが出てるけど、「アプリをモジュール使ってrewriteしよう」というセッション。

どうやってAndroidアプリをモジュール化するかがテーマ。

なぜモジュール化する?

  • エンジニアが増えてきたとき、お互いの作業をストップさせないようにするため。
  • メンテナス性のため。1つのでかいアプリだと、レイアウトXMLの名前だけみても面倒なことになるが、モジュール化しておけばだいぶ解決する
  • コンパイル速度をあげるため。

ビジネス面でもモジュール化は大事。

  • App bundle
  • Dynamic features

例えばPMが「イケてるライブラリ見つけてきたぜ!でも10MBもあるけどな!」という話を持ってきたとき、モジュール化していればdynamic featureとして作っておき、A/Bテストで一部のユーザーにだけ使わせるといった手法がとれる。

どうやってモジュール化するか?

切り口は2つ。Feature(機能)とLayer(レイヤー)。

Feature

ベースアプリは単なるアプリ

// :app
apply plugin 'com.android.application'

モジュールはAndroidライブラリにする。

// :module1
apply plugin 'com.android.library'

そしてベースアプリはmodule1に依存するようにする。

// :app
implementation project(':module1')

モジュールはDynamic featureとして作ることもできる。その場合は次のようにする。

// :module2
apply plugin 'com.android.dynamic-feature'

この場合、モジュール側がベースアプリに依存するようにする(この依存が気持ち悪いという意見も)。

// :module2
implementation project(':app')

ベースアプリはDynamic featureとして用意したmodule2にはアクセスできない点に注意(循環参照になっちゃう)。

Dynamic featureはオンデマンドでのダウンロードもできる。デフォルトではoffなので、apkに含まれる。

もしモジュール間で共通して使いたいコードがある場合は、ベースアプリに持たせるのではなく、coreといった別モジュールを用意しよう。

どの機能をDynamic feature(onDemand)にするか?パレートの法則(80:20の法則)にしたがってみるのがよさそう。ほとんどのユーザーが使う20%ほどの機能はライブラリモジュールとし、それ以外をDynamic featureにしてみる。

レイヤー

ボトムアップで考えてみよう。Roomを使うようなデータベースモジュール。Webサービスとの通信とかをやるWebサービスモジュール。それらをまとめたリポジトリモジュール。リポジトリモジュールの上にUIモジュール(図がいるな。。)。

apiとimplementation

モジュールAがモジュールBに依存している場合。

  • apiの場合:モジュールBはモジュールAのpublic APIの一部
  • implementationの場合:モジュールBはモジュールAの実装の一部(=どんな処理するかまでモジュールAは知っている)

Fake implementation

「モックを作る」という言い方のほうがわかりやすいかも。テスト時にモック用実装に差し替えれるよね!

既存アプリはどうする?

まずはレイヤー化からやってみよう。機能でわけるのはそれから。

Dynamic featureを使おう

ナビゲーション

ベースアプリからDyamic featureは見れないので、

  • Intentを投げる時は文字列でクラス名を指定
  • Fragmentの場合は Class.forName()

JetpackのNavigationでは、今修正中(なのかな

データベース

One-common database

扱いは簡単だけど、全モジュールのDBを抱え込むので分離しようという流れに逆行しちゃう。

モジュール別

分離はできてるけど、「AとBに同時に書き込みたい」みたいな要求がくるとつらい。またDBコネクションもたくさん作っちゃう。

ハイブリッド

くっつけておきたいものを1つにしておく作戦。柔軟だが柔軟すぎて扱いが難しい。

Roomではうまいことマージできないか、改善中。

テストの話

モジュール化すれば非Androidのモジュール(=テストが早く実行できる)も作れるが、それを目的としてモジュール分けするのは得策ではない(単に難しい)。Robolectricがあるのでそっち使おう。

一般的に、依存関係は少なくしよう。またビジネスロジックがUIに紐づかないように といったことも考えよう。

「このアプリの構造はすばらしい」というコメントで★5をつけるユーザーはいない。ユーザーをハッピーにするのが一番大事。