Android プロジェクトのモジュール化の検討
公開: 2019-08-21プロジェクトが一定の規模に達すると、単一のモジュールでさらに作業を進めると効果が低下します。 それをモジュール化することは、効果的なソリューションになります。
モジュール化の4つのメリット
モジュール化を決定する前に、何が関係しているかを明確にしておくことをお勧めします。 モジュラー Android プロジェクト構造の利点は次のとおりです。
コード分離の改善
すべてのモジュールは、公開 API インターフェースを公開し、実装の詳細を隠すことができます。 単一のモジュールでは、その実装が十分に隠されていることを完全に確認することはできません (特に、パッケージの可視性修飾子が使用できない Kotlin では)。
新技術の評価が容易に
モジュールを作成すると、他のモジュールに影響を与えることなく、新しいアーキテクチャ パターンまたは新しいライブラリを確認できます。
プロジェクトのビルド時間の短縮
モジュールを変更するには、そのモジュールとそれに依存する他のモジュールを再構築する必要があります。 このドキュメントを読んで、それがどのように機能するかを正確に確認してください: Android 開発者。 依存関係の構成
より便利な作業
より小さく分離されたコード片の分析/デバッグ/リファクタリングが容易になります。 また、新しい開発者のオンボーディングがより速く進みます。
これらの利点は、モジュール化プロセスを開始するのに十分なように思えますが、どのように開始しますか?
#1: モジュールとその関係を特定する
モジュールを認識するには、機能別とレイヤー別の 2 つの方法があります。
機能モジュールでは、ユーザーが利用できるアプリの領域 (ログイン、ダッシュボード、プロファイルなど) を理解できます。 これらの領域は、単一の画面またはいくつかのプロセスをカバーする画面のフローで構成できます。 このタイプのモジュールは、同じタイプのモジュールに依存できません。
機能を特定したら、いくつかのモジュールまたはすべてのモジュールで必要とされる共通の機能を抽出する必要があります。 これらのモジュールは、個別のアーキテクチャ レイヤー (永続ストレージ、ネットワーク、ナビゲーション、UI コンポーネントなど)、または一連の機能で使用される一部のデータを処理するビジネス ロジックを担当できます。 これらの種類のモジュールは通常、ライブラリと呼ばれます。 ライブラリ モジュールは、依存関係ツリーを構築できます。
機能モジュールとライブラリ モジュールに加えて、他のモジュール間の水平方向の接続を管理するための 1 つのモジュールも必要です (これについては次のポイントで詳しく説明します)。 このモジュールには、カスタム アプリケーション クラスと依存性注入のセットアップが含まれます。 他のモジュールはこのモジュールに依存できませんが、このモジュールはプロジェクト内の他のすべてのモジュールに依存しています。

上記の定義を考慮すると、モジュール階層は次のようになります。
#2: 依存性注入のセットアップ
プロジェクト モジュール間の依存関係にもかかわらず、ダガーの依存関係も設定する必要があります。 Dagger は依存関係を宣言する 2 つの方法を提供します:サブコンポーネントとコンポーネント依存関係です。
Dagger サブコンポーネントの依存関係では、親が依存するすべての子を宣言する必要があります。 プロジェクト モジュール間では、プロジェクト モジュールの依存関係の方向が逆になるため、この種の関係は機能しません。 ただし、別のプロジェクト モジュール内で使用できます。
Dagger コンポーネントの依存関係は、子が親に依存していることを宣言できるため、より柔軟です。 これにより、個別のプロジェクト モジュール間でこの種の依存関係を使用できます。
ある時点で、あるモジュールが別のモジュールに関する限られた知識しか必要としないことに気付くかもしれません。 これの非常に良い例は、機能モジュール間のナビゲーションです。 この種の関係を提供することは、しばしば水平依存性と呼ばれます。 別々のモジュール間でこの通信チャネルを作成するには、この通信を記述するインターフェイスを持つ追加のモジュールと、実装を宣言されたインターフェイスにバインドするモジュールが必要です。
水平方向の依存関係を管理するためのプロジェクト モジュールの依存関係のセットアップは、次の図に示されています。

このような関係のサンプル コードは、記事の最後にあるプロジェクトで提供されています。

#3:Gradleのセットアップ
すべてのプロジェクト モジュールには独自の gradle.build があり、追加された依存関係とプラグインが適用されることを除いて、ほとんど同じです。 したがって、反復的な構成をプロジェクト ディレクトリのルートにある 1 つの gradle ファイルに抽出すると便利です。 このようなファイルは、一般的な gradle タスクを登録して、静的コード分析や単体テストを実行することもできます。
このような一般的な設定のコード スニペットは次の場所にあります。
afterEvaluate { project -> def isAndroid = project.plugins.hasPlugin('com.android.library') || project.plugins.hasPlugin('com.android.application') setupModule(isAndroid) setupCommonTestDependencies(isAndroid) setupCommonTasks(isAndroid) } def setupModule(isAndroid) { if (isAndroid) { android { compileSdkVersion projectCompileSdk defaultConfig { minSdkVersion projectMinSdk targetSdkVersion projectTargetSdk } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { abortOnError true checkReleaseBuilds false checkAllWarnings true warningsAsErrors true def lintBaseline = file("quality/lint-baseline.xml") if (lintBaseline.exists()) baseline lintBaseline } } } else { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } } def setupCommonTestDependencies(isAndroid) { dependencies { testImplementation "junit:junit:${junitVersion}" testImplementation "org.assertj:assertj-core:${assertJVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}" testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:${mockitoKotlinVersion}" if (isAndroid) { androidTestImplementation "androidx.test.ext:junit:${axTestJUnitVersion}" androidTestImplementation "androidx.test.espresso:espresso-core:${axEspressoLibVersion}" } } } def setupCommonTasks(isAndroid) { if (isAndroid) { tasks.register("unitTest") { task -> task.dependsOn(testDebugUnitTest) } } else { tasks.register("unitTest") { task -> task.dependsOn(test) } } }
結論
この記事は、Android プロジェクトをモジュール化するための完全なガイドではありません。 しかし、プロジェクトのモジュール化を開始するときに考慮すべき側面に取り組んでいると思います。
水平方向の依存関係を示すコードは、リンクにあります。
Android 用のネイティブ アプリケーションを構築したいですか。 ミキドをチョイス!