web-dev-qa-db-fra.com

Portée et sous-composants de Dagger 2

J'essaie d'améliorer mon application et de coder plus facilement en utilisant Dagger2 J'ai eu une idée générale, mais je n'arrive toujours pas à comprendre comment les portées sont gérées par Dagger2 J'ai injecté une dague dans mon projet (ça a l'air drôle) . J'ai créé le composant ApplicationComonent et cela fonctionne parfaitement dans mon projet. Voici mon code.

@Singleton
@Component(modules = {
        ApplicationModule.class,
        ThreadingModule.class,
        NetworkModule.class,
        DatabaseModule.class,
        ServiceModule.class,
        ParseModule.class,
        PreferencesSessionModule.class})

public interface ApplicationComponent {
    ActivityComponent activityComponent(ActivityModule activityModule);

    void inject(BaseActivity baseActivity);

    void inject(MainAppActivity mainAppActivity);

    void inject(MyApplication application);

    void inject(BaseFragment baseFragment);

    void inject(MyService service);

    void inject(RegistrationIntentService service);
}

Je crée mon instance de composant dans la classe MyApplication comme ceci

private void initializeAndInjectComponent() {
        mApplicationComponent =
                DaggerApplicationComponent
                        .builder()
                        .threadingModule(new ThreadingModule(1))
                        .applicationModule(new ApplicationModule(this))
                        .networkModule(new NetworkModule(
                                MyService.API_SERVER_BASE_URL,
                                MyService.TIMEOUT))
                        .build();
        mApplicationComponent.inject(this);
    }

Et je peux obtenir un composant afin d'injecter dans mon Activities

    MyApplication application = MyApplication.get(this);
    application.getApplicationComponent().inject(this);

Tout fonctionne parfaitement.

Pour ajouter chaque méthode ainsi que la classe de module est annotée avec la portée @Singleton, Tous les modules liés à ApplicationComponent

Maintenant, je veux améliorer les dépendances et j'ai vu beaucoup d'exemples avec des étendues personnalisées comme @PerActivity, @PerFragment. J'ai beaucoup de questions, mais à ce sujet plus tard.

J'ai donc créé ActivityComponent

@PerActivity
@Subcomponent(
        modules = {
                NetworkServiceModule.class,
                ActivityModule.class,
                PermissionModule.class
        })
public interface ActivityComponent {
    Activity activity();

    void inject(BaseActivity baseActivity);
}

Tous les modules ressemblent à ceci

@PerActivity
@Module
public class ActivityModule {
    private Activity mActivity;

    public ActivityModule(Activity activity) {
        this.mActivity = activity;
    }

    @Provides
    @PerActivity
    Activity provideActivity() {
        return this.mActivity;
    }
}

J'ai les dépendances suivantes dans mon BaseActivity

// Dependencies from ApplicationComponent
    @Inject
    protected ApplicationSettingsManager mApplicationSettingsManager;
    @Inject
    protected ScheduledThreadPoolExecutor mPoolExecutor;
// Dependencies from ActivityComponent
    @Inject
    protected SpiceManager mSpiceManager;
    @Inject
    protected PermissionController mPermissionController;

Et dans ma méthode onCreate() j'injecte comme suit

    MyApplication application = MyApplication.get(this);
    application.getApplicationComponent().activityComponent(new ActivityModule(this)).inject(this);

Avant de créer le sous-composant ActivityComponent, il était

   MyApplication application = MyApplication.get(this);
        application.getApplicationComponent().inject(this);

Maintenant j'ai une erreur

Error:(34, 10) error: com.octo.Android.robospice.SpiceManager cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
BaseActivity.mSpiceManager
[injected field of type: com.octo.Android.robospice.SpiceManager mSpiceManager]

Je ne peux pas comprendre où est le problème, ce que j'ai manqué. Mes questions sur les étendues dans dagger2.

Tout sauf @Singleton Est ignoré par Dagger 2, ai-je raison? Je ne comprends pas comment la durée de vie du composant est gérée? Je n'ai qu'une idée

  1. Lorsque vous utilisez @Singleton La dague d'annotation crée un objet dans un pool statique qui existera pendant tout le cycle de vie de l'application et sera détruit lorsque l'instance du processus JVM (dalvik VM, ART) sera détruite.

  2. Lorsque vous utilisez une autre annotation est juste pour vous en tant que développeur pour mieux maintenir le code, @PerActivity, @PerFragment Est juste une annotation personnalisée rien de plus. Et si vous placez le composant @PerFragment Dans la classe Application, il vivra aussi longtemps que l'Application sera en vie. Ai-je raison ?

  3. Donc, je comprends cela comme ceci, si le poignard trouve l'annotation @Singleton, Il ajoutera une référence statique au composant lors de sa première création et dans le cas de toute autre annotation, il ne contiendra pas de référence au composant.

Je serais très reconnaissant pour toute aide concernant les problèmes décrits ci-dessus.

MISE À JOUR

Merci David Medenjak Pour cette excellente réponse, j'ai bien mieux compris Dagger2.

Je viens également de trouver le problème, dans la mesure où j'utilise maintenant un composant Activity séparé, j'ai oublié deux lignes dans ApplicationComponent et j'ai changé inejction dans mon MainActivity en ActivityComponent au lieu de ApplicationComponent, donc à coup sûr, il n'a pas pu résoudre les dépendances du sous-composant.

 void inject(BaseActivity baseActivity);

 void inject(MainAppActivity mainAppActivity);

Maintenant, tout fonctionne parfaitement, j'aime Dagger2 Et l'architecture séparée.

32
CROSP

Un peu radical, mais pour simplifier les choses: Toutes les annotations Scope ne sont rien d'autre que du sucre syntaxique, y compris @Singleton.

Les étendues fournissent principalement des contrôles de temps de compilation. Dépendances cycliques ou erreurs sur des choses que vous pourriez avoir manquées. @Singleton est comme n'importe quelle autre étendue, la seule différence est qu'il s'agit d'une annotation déjà existante et que vous n'avez pas à la créer vous-même. Vous pouvez simplement utiliser @MySingleton au lieu.

[...] la dague crée un objet dans un pool statique qui existera pendant tout le cycle de vie de l'application

Non. Dagger ne fait rien statique. Vous avez des objets composants. Ces composants contiennent vos objets créés par des modules. Si un objet dans un composant a la portée du composant, il ne sera créé qu'une seule fois dans ce composant exact . Si vous décidez de créer 2 AppComponent objets, vous aurez 2 objets de chaque @Singleton objet annoté, chacun dans son composant. C'est pourquoi vous devriez garder la référence au composant. La plupart des implémentations que j'ai vues ou utilisées gardent donc leur AppComponent dans leur Application. Si vous faites cela, vous pouvez l'utiliser comme un singleton — ce n'est encore qu'un POJO.

[...] vous placez le composant @PerFragment dans la classe Application, il vivra aussi longtemps que l'Application vivra.

Oui. Comme déjà couvert par le paragraphe ci-dessus, ce n'est qu'un objet. Gardez la référence, vous gardez les objets. Jetez-le ou créez-en un et vous avez de nouveaux objets (définis dans ce composant/portée). Vous devez cependant ne pas garder les activités ou les composants à portée limitée respectivement dans les activités ou les fragments, car les conserver par exemple dans votre composant d'application entraînera très probablement une fuite de mémoire. (Si ce n'est pas le cas, vous n'auriez probablement pas eu besoin de la portée de l'activité ou du fragment.)

si le poignard trouve @Singleton annotation, il ajoutera une référence statique au composant lors de sa première création et dans le cas de toute autre annotation, il ne contiendra pas de référence au composant.

Encore une fois, non. Rien de statique. Plain old Java objects. Vous pouvez avoir plusieurs @Singleton composants avec leurs propres objets, mais ce n'est probablement pas le cas (bien que ce soit ce qui rend les tests d'instrumentation possibles/faciles - il suffit d'échanger les composants.)


Votre erreur mentionnée

SpiceManager ne peut pas être fourni sans un constructeur @Inject ou à partir d'une méthode annotée @ Provides ou @ Produces.

Cela signifie que le composant avec lequel vous essayez d'injecter votre objet ne peut trouver aucun moyen de produire ou de fournir un SpiceManager. Assurez-vous de le fournir depuis votre AppComponent ou un autre endroit, ne manque aucune annotation, etc.

60
David Medenjak