web-dev-qa-db-fra.com

Dague 2: fournir la même instance entre plusieurs composants avec la même portée sur différents modules de bibliothèque

J'ai une Core Android Library où je définis une annonce CoreComponent en utilisant la portée @Singleton pour injecter des instances de classes fournies par un CoreModule.

@Singleton
@Component(modules = {CoreModule.class})
public interface CoreComponent {
    void inject(SomeClass target);
}

@Module
public class CoreModule {
    @Singleton
    @Provides
    CoreRepository provideCoreRepository() {
        return new CoreRepositoryImpl();
    }
}

Je voudrais accéder aux mêmes instances @Singleton à partir d'une autre Android qui dépend de la bibliothèque principale et utilise un autre composant.

@Singleton
@FooScope
@Component(modules = {CoreModule.class, FooModule.class})
public interface FooComponent {
    void inject(SomeActivity target);
}

public class FooActivity extends AppCompatActivity {
    @Inject
    public CoreRepository repo;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        injectDependencies();
        super.onCreate(savedInstanceState);
    }
    [...]
}

Le code ci-dessus est généré mais la portée @Singleton est "locale" pour le composant. En d'autres termes, il existe deux instances singleton, l'une pour le CoreComponent et l'autre pour le FooComponent.

Android Application
├── Foo Library
|   └── Core Library
├── Bar Library
|   └── Core Library
·
·
·
└── Core Library

Je pense que la meilleure solution devrait être d'utiliser un sous-composant mais, malheureusement, cela ne semble pas possible car la Core Library n'a pas de visibilité sur les autres bibliothèques.

Existe-t-il un autre moyen de partager avec Dagger la même instance d'une classe entre les composants si la classe est annotée avec la même portée?

20
Roberto Leinardi

Supprimez les sites d'injection de votre CoreComponent - il a désormais pour seule fonction d'exposer la liaison de CoreRepository à ses composants dépendants:

@Singleton
@Component(modules = {CoreModule.class})
public interface CoreComponent {
    CoreRepository coreRepository();
}

Créez une référence à ce composant de portée singleton dans votre application:

public class MyApplication extends Application {
    private final CoreComponent coreComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        coreComponent = DaggerCoreComponent
                            .coreModule(new CoreModule())
                            .build();
    }

    public static CoreComponent getCoreComponent(Context context) {
        return ((MyApplication) context.getApplicationContext()).coreComponent;
    }
}

Créez une nouvelle portée plus étroite:

@Scope
@Retention(RetentionPolicy.RUNTIME) public @interface PerActivity {}

Créez un nouveau composant qui suit cette portée avec les sites d'injection souhaités:

@PerActivity
@Component(dependencies = {CoreComponent.class})
public interface ActivityComponent {
    void inject(FooActivity activity);

    void inject(BarActivity activity);
}

Lorsque vous accédez à ce composant à portée d'activité dans le site d'injection, vous devez fournir l'instance de CoreComponent au générateur. Vous pouvez maintenant injecter dans votre Activity

public class FooActivity extends AppCompatActivity {
        @Inject
        public CoreRepository repo;

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            CoreComponent coreComponent = MyApplication.getCoreComponent(this);
            DaggerActivityComponent.builder()
                .coreComponent(coreComponent)
                .build()
                .inject(this);
        }
    }
}
18
David Rawson