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?
Donc, après beaucoup d'essais, j'ai finalement réussi à résoudre mon problème.
Ce que j'ai découvert c'est:
J'ai trouvé cet article vraiment utile: https://proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc
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
}
}