Ein Beispiel für eine modulare Android-Anwendung mit der Navigationskomponente und Koin (DI)

Entwickler, hallo!





In diesem Artikel möchte ich ein Beispiel für eine modulare Android-Anwendung mit NavComponent (JetPack) und Koin (DI) vorstellen.





Wir haben viele verschiedene Android-Projekte in unserem Unternehmen, die die Funktionen des anderen nutzen sollten - dies ist eine Art Ökosystem. Um dies zu erreichen, müssen wir diese Funktionen so unabhängig und flexibel wie möglich entwickeln.





Die Anforderungen an Funktionen können wie folgt formuliert werden:





  1. Eine Funktion sollte in der Lage sein, Logik und Benutzeroberfläche unabhängig voneinander zu ersetzen.





  2. Um ein Feature aufzurufen, sollte es ausreichen, seine Schnittstelle (Beschriftung) und die erforderlichen Eingabe- / Ausgabeparameter zu kennen.





  3. Das Feature selbst muss seine Implementierung vor anderen Features verbergen.





  4. Unterstützung für sofort ein- und ausgeschaltetes Feature.





Für mich ist eine Funktion ein logisch kombiniertes System von Komponenten, das eine Funktion in einer Anwendung ausführt. Zum Beispiel Autorisierung, Kauf. Ein Feature enthält ein eigenes UI-Diagramm (eine Kette von Fragmenten), das im Navigations-BackStack gespeichert werden soll. Nach dem Beenden des Features kehren Sie zum Benutzer zum Aufrufpunkt des Features zurück.





Ein bedingtes Schema einer Anwendung, das aus zwei Merkmalen besteht:





Das Diagramm zeigt eine Anwendung mit einem MainFragment (M) und zwei Funktionen (A und B). Von M aus können Sie zu den Merkmalen A und B gehen. Von Merkmal B aus können wir zu Merkmal A gelangen. Außerdem müssen wir nach Abschluss von Merkmal A zum Anlaufpunkt zurückkehren: Wenn wir von M zu Merkmal A gelangen, dann Wir kehren zu M zurück, und wenn Feature A von B aufgerufen wird, dann zu B. In diesem Fall muss der Navigationsstapel erhalten bleiben. A1, A2, B1, B2 - Merkmalsfragmente.





: 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 und AppNavigator können erweitert werden, hier habe ich das einfachste Beispiel gezeigt. Zum Beispiel müssen Sie definitiv Parameter übergeben und ein Ergebnis zurückgeben. Dies kann auch über ModuleNavInfo erfolgen. Um beispielsweise das Ergebnis eines Features zurückzugeben, können Sie StateFlow hinzufügen und abonnieren. Alles fällt prägnant und an einem Ort aus. Sie können die Interaktion auch mithilfe interner Dienste konfigurieren, die in BL-Modulen vor der Außenwelt verborgen werden können.





Der Beispielcode ist auf github verfügbar .





Vielen Dank für Ihre Aufmerksamkeit!








All Articles