Un ejemplo de una aplicación de Android modular que utiliza el componente de navegación y Koin (DI)

Desarrollador, hola!





En este artículo, quiero compartir un ejemplo de una aplicación de Android modular que usa NavComponent (JetPack) y Koin (DI).





Tenemos muchos proyectos de Android diferentes en nuestra empresa que deberían usar las características de los demás; este es un tipo de ecosistema. Para lograr esto, necesitamos desarrollar estas características de la manera más independiente y flexible posible.





Los requisitos para las características se pueden formular de la siguiente manera:





  1. Una función debería poder reemplazar la lógica y la interfaz de usuario de forma independiente.





  2. Para llamar a una función, debería ser suficiente conocer su interfaz (etiqueta) y los parámetros de entrada / salida necesarios.





  3. La función en sí debe ocultar su implementación de otras funciones.





  4. Soporte para función on-off fuera de la caja.





Para mí, una característica es un sistema de componentes combinado lógicamente que realiza alguna función en una aplicación. Por ejemplo, autorización, compra. Una función contiene su propio gráfico de IU (una cadena de fragmentos), que debe almacenarse en el backStack de navegación y, después de salir de la función, devolver al usuario al punto de llamada de la función.





Un esquema condicional de una aplicación que consta de dos características:





El diagrama muestra una aplicación que tiene un MainFragment (M) y dos características (A y B). Desde M puede ir a las funciones A y B. Y desde la función B podemos llegar a la función A. Además, después de completar la función A, debemos volver al punto de llamada: si llegamos a la función A desde M, entonces regresamos a M, y si se llama a la característica A desde B, luego a B. En este caso, la pila de navegación debe conservarse. A1, A2, B1, B2: fragmentos de características.





: 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 y AppNavigator se pueden extender, aquí mostré el ejemplo más simple. Por ejemplo, definitivamente necesita pasar parámetros y devolver un resultado. Esto también se puede hacer a través de ModuleNavInfo. Por ejemplo, para devolver el resultado de una función, puede agregar StateFlow y suscribirse a ella. Todo resulta conciso y en un solo lugar. También puede configurar la interacción utilizando servicios internos que se pueden ocultar del mundo exterior en los módulos BL.





El código de ejemplo está disponible en github .





¡Gracias por la atención!








All Articles