web-dev-qa-db-fra.com

Dépendances du sous-composant Access Dagger à partir du module Android parent

Récemment, j'ai commencé à diviser notre application en modules Android plus petits, mais j'ai du mal à faire en sorte que Dagger fonctionne comme je le souhaite.

Ma configuration actuelle de poignard comprend:
- ApplicationComponent marqué avec @Singleton. Ce composant est créé au démarrage de l'application.
- UserSubComponent marqué avec @UserScope. Ce sous-composant est créé lors de la connexion de l'utilisateur.

Ces deux composants sont placés dans mon module app avec ma classe App qui est responsable de la création des deux composants. 

Dans mon module login (qui est un parent de mon module d'application, de sorte qu'il ne peut accéder à rien dans le module d'application), j'ai ma AuthenticationManager. Lorsque l’utilisateur se connecte, j’utilise RxJava pour signaler de AuthenticationManager à App, afin que UserSubComponent puisse être créé.

Mon problème est que je dois accéder à certaines dépendances de ma UserSubComponent, après sa création, dans ma AuthenticationManager afin de pouvoir précharger les données de l'utilisateur avant de continuer.

Structure du module:

              app (AppComponent & UserSubComponent)
                                ^
                                |
  login (AuthenticationManager) - feature 2 - feature 3

Mon cours d'application:

class App : DaggerApplication() {

    @Inject
    lateinit var authenticationManager: AuthenticationManager

    override fun onCreate() {
        super.onCreate()

        authenticationManager
            .authenticationStateStream
            .subscribe { state ->
                if (state == AuthenticationState.AUTHENTICATED) {
                    AppInjector.userComponent.inject(this)
                }
    }
}

AuthenticationManager:

class AuthenticationManager @Inject constructor(loginApi: LoginApi) {

    @Inject
    lateinit var preLoader : PreLoader // This won't work because of different scope

    val authenticationStateStream = Observable<AuthenticationState>()

    fun login() {
        if (success) {
            authenticationStateStream.emit(AuthenticationState.AUTHENTICATED)
            // UserSubComponent is now created
            preLoader.preload()
        }
    }
}

Composant de l'application

@Singleton
@Component(modules = [AppModule::class, AndroidSupportInjectionModule::class])
interface AppComponent : AndroidInjector<App> {

    fun userComponentBuilder(): UserComponent.Builder
}

AppModule

@Module
class AppModule {
    @Provides
    @Singleton
    fun provideLoginApi() = LoginApi()
}

UserSubComponent

@UserScope
@Subcomponent(modules = [UserModule::class, AndroidSupportInjectionModule::class])
interface UserComponent : AndroidInjector<App> {

    @Subcomponent.Builder
    interface Builder {
        fun build(): UserComponent
    }

}

UserModule

@Module
class UserModule {
    @Provides
    @UserScope
    fun providesPreLoader() = PreLoader()
}

Puis-je en quelque sorte obtenir cette structure au travail? Ou quelles sont mes options en matière de modules + poignard?

11
Eixx

Donc, après beaucoup d'essais, j'ai finalement réussi à résoudre mon problème.

Ce que j'ai découvert c'est: 

  • N'utilisez pas de sous-composants lorsque votre application est divisée en plusieurs modèles.
  • Chaque modèle a besoin de son propre composant (composant d'entité)

J'ai trouvé cet article vraiment utile: https://proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc

0
Eixx

Lorsque Dagger essaie de créer un objet de AuthenticationManager, il essaiera également d'injecter tous les champs annotés @Inject. Et cela ne fonctionne pas car vous avez correctement souligné que PreLoader est issu d’un domaine différent, ce qui signifie essentiellement qu’il provient d’un composant différent, c’est-à-dire UserComponent.

Ce qu'il est important de noter ici, c'est que UserComponent n'est pas encore créé au moment où l'objet AuthenticationManager est d'abord créé par Dagger. Et puisque PreLoader est fourni parUserComponent, Dagger n’a aucun moyen d’injecter ce champ. Cependant, vous pouvez injecter ce champ dans AuthenticationManager par vous-même.

Quelque chose comme ça:

class AuthenticationManager @Inject constructor(loginApi: LoginApi) {

    // Remove the @Inject annotation and provide a custom setter which will pre-load user data 
    private lateinit var preLoader : PreLoader
    set(preLoader) {
        field = preLoader
        preLoader.preload()
    }

    val authenticationStateStream = Observable<AuthenticationState>()

    fun login() {
        if (success) {
            authenticationStateStream.emit(AuthenticationState.AUTHENTICATED)
            // UserSubComponent is now created
            // You can't do this here:
            // preLoader.preload()
        }
    }
}

Votre classe d'application:

class App : DaggerApplication() {

    @Inject
    lateinit var authenticationManager: AuthenticationManager

    override fun onCreate() {
        super.onCreate()

        authenticationManager
            .authenticationStateStream
            .subscribe { state ->
                if (state == AuthenticationState.AUTHENTICATED) {
                    // Here you can directly set the PreLoader object yourself
                    authenticationManager.preLoader = 
                                AppInjector.userComponent.preLoader()
                }
             }
    }
}

Afin d'accéder à l'objet PreLoader, vous devez également modifier votre UserComponent comme ceci:

@UserScope
@Subcomponent(modules = [UserModule::class, AndroidSupportInjectionModule::class])
interface UserComponent : AndroidInjector<App> {

    fun preLoader() : PreLoader // <-- Need to add this

    @Subcomponent.Builder
    interface Builder {
        fun build(): UserComponent
    }

}
0
Abhishek Jain