ナビゲーションコンポーネントとKoin(DI)を使用したモジュラーAndroidアプリケーションの例

開発者、こんにちは!





この記事では、NavComponent(JetPack)とKoin(DI)を使用したモジュラーAndroidアプリケーションの例を共有したいと思います。





私たちの会社には、お互いの機能を使用する必要のあるさまざまなAndroidプロジェクトがあります。これは一種のエコシステムです。これを実現するには、これらの機能を可能な限り独立して柔軟に開発する必要があります。





機能の要件は、次のように定式化できます。





  1. 機能は、ロジックとUIを互いに独立して置き換えることができる必要があります。





  2. フィーチャーを呼び出すには、そのインターフェース(ラベル)と必要な入力/出力パラメーターを知っていれば十分です。





  3. 機能自体は、その実装を他の機能から隠す必要があります。





  4. 箱から出してすぐに使える機能のサポート。





私にとって、機能とは、アプリケーションで何らかの機能を実行する、論理的に組み合わされたコンポーネントのシステムです。たとえば、承認、購入。機能には独自のUIグラフ(フラグメントのチェーン)が含まれており、ナビゲーションbackStackに保存する必要があります。機能を終了した後、ユーザーを機能の呼び出しポイントに戻します。





2つの機能で構成されるアプリケーションの条件付きスキーム:





この図は、MainFragment(M)と2つの機能(AとB)を持つアプリケーションを示しています。Mから機能AとBに移動できます。また、機能Bから機能Aに移動できます。さらに、機能Aの完了後、呼び出しポイントに戻る必要があります。Mから機能Aに移動した場合は、 Mに戻り、Bから機能Aを呼び出した場合は、Bに戻ります。この場合、ナビゲーションスタックを保持する必要があります。A1、A2、B1、B2-機能フラグメント。





: API, BL, UI.





.  A -> B - .





  • API , . Lint : API .





  • BL - . API . internal - , DI , . Lint : BL   APP .





  • UI . internal - , DI , NavGraph, nested graph. Lint : UI APP .





:









  • (app)





  • app DI





startKoin {
            modules(
                listOf(
                    AppKoinModule.create(),                 
                    FeatureAImplKoinModule.create(),
                    FeatureAUiKoinModule.create(),
                    FeatureBImplKoinModule.create(),
                    FeatureBUiKoinModule.create()
                )
            )
        }
      
      



, , app DI, , . C UI - , root app .





  • root (app). , , .





  • :





 appNavigator.navigateTo(FeatureADestination::class.java)
      
      



( )





interface FeatureADestination : ModuleNavInfo
      
      



ModuleNavInfo , . FeatureADestination UI .





interface ModuleNavInfo {
    fun getNavigationStartPointResId(): Int

    fun isFeatureAvailable(): Boolean
}
      
      



navigator, . , DI ModuleNavInfo:





class KoinAppNavigator : AppNavigator {

    private val navigationDestinationInternal = MutableLiveEvent<EventArgs<ModuleNavInfo>>()
    override val navigationDestination =
        navigationDestinationInternal as LiveData<EventArgs<ModuleNavInfo>>

    private val navigationIntDestinationInternal = MutableLiveEvent<EventArgs<Int>>()
    override val navigationResDestination: LiveData<EventArgs<Int>>
        get() = navigationIntDestinationInternal

    override fun <T : ModuleNavInfo> navigateTo(
        moduleNavInfo: Class<T>
    ) {
        val destination = KoinJavaComponent.get(moduleNavInfo) as ModuleNavInfo
        navigationDestinationInternal.postValue(EventArgs(destination))
    }

    override fun navigateTo(destination: Int) {
        navigationIntDestinationInternal.postValue(EventArgs(destination))
    }

    override fun <T : ModuleNavInfo> resolveModule(moduleNavInfo: Class<T>): ModuleNavInfo? {
        return try {
            KoinJavaComponent.get(moduleNavInfo)
        } catch (e: Exception) {
            null
        }
    }

    override fun <T : ModuleNavInfo> isCanNavigateTo(moduleNavInfo: Class<T>): Boolean {
        return resolveModule(moduleNavInfo) != null
    }
}

      
      



ModuleNavInfoとAppNavigatorは拡張できます。ここでは、最も簡単な例を示しました。たとえば、パラメータを渡して結果を返す必要があります。これは、ModuleNavInfoを介して実行することもできます。たとえば、機能の結果を返すには、StateFlowを追加してサブスクライブできます。すべてが簡潔に1か所にまとめられます。BLモジュールで外部から隠すことができる内部サービスを使用して対話を構成することもできます。





サンプルコードはgithubで入手できます





ご清聴ありがとうございました!








All Articles