web-dev-qa-db-fra.com

Constructeur d'injection Dagger 2

Je commence à utiliser Dagger 2 dans une application que je développe, mais j'ai des questions sur le fonctionnement de Dagger 2.

J'obtiens toute la logique derrière les méthodes @Provides et l'annotation @Inject pour initialiser vos dépendances, mais l'annotation @Inject aux constructeurs de classes me dérange un peu.

Par exemple:

Im mon application, j'ai un module défini, le ContextModule, pour récupérer le contexte de mon application:

ContextModule.Java

@Module
public class ContextModule {

    private final Context context;

    public ContextModule(Context context) {
        this.context = context;
    }

    @Provides
    public Context context() {
        return this.context;
    }
}

Ce module est utilisé par mon BaseActivityComponent:

BaseActivityComponent.Java

@BaseActivityScope
@Component(modules = ContextModule.class)
public interface BaseActivityComponent {
    void injectBaseActivity(BaseActivity baseActivity);
}

Jusqu'ici tout va bien .. alors j'ai une classe AuthController, cela dépend du contexte et je veux l'injecter dans ma BaseActivity. Donc, dans mon AuthControllers.class, j'ai quelque chose comme:

public class AuthController {

    private Context context;

    @Inject
    public AuthController(Context context) {
        this.context = context;
    }

    public void auth() {
        // DO STUFF WITH CONTEXT
    }
}

Et je l'injecte dans ma BaseActivity comme:

public class BaseActivity extends AppCompatActivity {

    @Inject
    AuthController authController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        BaseActivityComponent component = DaggerBaseActivityComponent.builder()
            .contextModule(new ContextModule(this))
            .build();

        component.injectBaseActivity(this);

        authController.auth();

    }
}

Maintenant ma question est, comment le poignard sait-il que mes AuthControllers sont une dépendance pour BaseActivity? Juste en déclarant

@Inject
AuthController authController;

c'est comme si j'avais créé un ControllerModule comme:

@Module(includes = ContextModule.class)
public class ControllerModule {

    @Provides
    AuthController authController(Context context) {
        return new AuthController(context);
    }

}

Et puis dans mon BaseActivityComponent, j'ajouterais mon getter AuthController et changerais mon module de dépendance en ControllersModule:

@BaseActivityScope
@Component(modules = ControllersModule.class)
public interface BaseActivityComponent {

    void injectBaseActivity(BaseActivity baseActivity);

    AuthController getAuthController();
}

Lorsque j'appelle injectBaseActivity (this), il "dit" à la dague que toutes les annotations @Inject sont des dépendances de ma classe, puis il recherche dans mon projet les constructeurs annotés @Inject qui correspondent à ce type?

Je pensais qu'une bonne chose à propos de Dagger 2 était que les fichiers du module pouvaient être utilisés comme "documentation" de mes trois dépendances. Mais si vous ajoutez simplement @Inject dans tous les constructeurs dont j'ai le contrôle, cela ne pourrait-il pas être un peu déroutant à l'avenir, car vous ne savez pas ce qui dépend de quoi? (Je veux dire, vous savez ce qui dépend de quoi, il vous suffit de parcourir beaucoup de fichiers pour vraiment le découvrir)

Existe-t-il des meilleures pratiques pour l'utilisation d'annotations @Inject dans les constructeurs ou pour ajouter la méthode @Provides dans les fichiers Modules? Je reçois cela en utilisant @Inject dans le constructeur, je n'ai pas besoin de changer la définition du constructeur dans mon fichier Module, mais y a-t-il un inconvénient?

Merci.

27
Thiago Loddi

Lorsque j'appelle injectBaseActivity (this), il "dit" à la dague que toutes les annotations @Inject sont des dépendances de ma classe, puis il recherche dans mon projet les constructeurs annotés @Inject qui correspondent à ce type?

Exactement. Mais cela ne se fait pas lorsque vous appelez injectBaseActivity, mais tout se passe pendant la compilation. C'est une façon de traitement d'annotation (une autre utilise la réflexion lors de l'exécution).

Lorsque vous générez votre projet, le processeur d'annotation-dague que vous incluez (en tant que dépendance) dans votre fichier build.gradle est appelé avec une liste de tous vos champs, classes, etc. annotés par le @Inject annotation et construit avec elle un graphe de dépendances. Il résout ensuite le graphique, générant du code source qui fournit toutes les dépendances pour les éléments du graphique.

injectBaseActivity exécute simplement le code qui a été généré auparavant et affecte toutes les dépendances à votre objet. Il s'agit du code source approprié, que vous pouvez lire et déboguer.

La raison pour laquelle il s'agit d'une étape de compilation - en termes simples - est la performance et la validation. (Par exemple, si vous avez un cycle de dépendance, vous obtenez une erreur de compilation)


comment dagger sait que mes AuthControllers sont une dépendance de BaseActivity?

@Inject
AuthController authController;

En annotant le champ @Inject le poignard sait que vous voulez un AuthController. Jusqu'ici tout va bien. Maintenant, poignard cherchera un moyen de fournir le contrôleur, le recherchant dans le composant, les dépendances des composants et les modules de composants. Il vérifiera également si la classe peut être fournie seule, car elle connaît son constructeur.

Comment le poignard connaît-il le constructeur d'objets si vous ne l'incluez dans aucun module?

@Inject
public AuthController(Context context) { /**/ }

En annotant le constructeur avec inject, vous avez également indiqué à dagger qu'il existe une classe appelée AuthController et que vous avez besoin d'un contexte pour qu'elle soit instanciée. C'est essentiellement la même chose que de l'ajouter à votre module.

Un module @Provides doit être utilisée si vous n'avez pas le code source pour simplement ajouter le @Inject annotation au constructeur, ou si l'objet a besoin d'une initialisation supplémentaire. Ou dans votre cas ...

[...] les fichiers du module pourraient être utilisés comme "documentation" de mon arbre de dépendances [...]

Oui, bien sûr, vous pouvez le faire. Mais au fur et à mesure que votre projet se développe, vous devrez conserver beaucoup de code inutile, car cela aurait pu être fait avec une simple annotation sur le constructeur.

Existe-t-il des meilleures pratiques pour l'utilisation d'annotations @Inject dans les constructeurs ou pour ajouter la méthode @Provides dans les fichiers Modules?

Si vous souhaitez fournir différentes versions pour un contexte différent (par exemple, implémenter une interface de 2 manières différentes), il y a aussi le @Binds annotation qui indique à la dague quelle classe vous souhaitez fournir comme implémentation.

En dehors de cela, je pense que vous devriez toujours utiliser l'injection de constructeur lorsque cela est possible. Si quelque chose change, vous n'avez pas à toucher à d'autres parties de votre code, et c'est juste moins de code que vous écrivez, et donc moins d'endroits où vous pourriez inclure un bogue.

Dagger peut également optimiser beaucoup de choses en en sachant plus, et si vous implémentez du code inutile, il devra fonctionner avec la surcharge que vous avez introduite.


Bien sûr, à la fin, tout dépend de ce que vous pensez être le mieux. Après tout, c'est vous qui devez travailler avec votre code ;)

35
David Medenjak