web-dev-qa-db-fra.com

Dague 2 injectant plusieurs instances du même type d'objet

Contexte

Je suis en train de convertir mon application en architecture MVP et trouve Dagger 2 utile pour injecter des dépendances en cas de besoin. Mon application doit communiquer avec deux sites Web (ma propre et une tierce partie). Il peut arriver que des demandes adressées à ma propre API et à la tierce partie API puissent être déclenchées en même temps. J'utilise Retrofit pour communiquer avec ces API et GSON pour la sérialisation/désérialisation.

Ce que j'ai fait auparavant

J'ai créé deux Retrofit RestAdapters et utilisé un modèle Service Locator pour les obtenir en cas de besoin. Le RestAdapter destiné à être utilisé pour mon propre api inclut GSONConverter avec certains TypeAdapters personnalisés, car je ne souhaite pas une désérialisation JSON 1: 1 de ma réponse dans l'application. L'autre RestAdapter est destiné à une API tierce et utilise un autre convertisseur GSON avec une stratégie de dénomination de champ spécifique.

Problème

J'essaie d'utiliser DI au lieu de Service Locator pour obtenir mon RestAdapter (et l'interface API). J'ai la configuration de ma classe NetModule comme suit

@Module
public class NetModule {

    private static final String MY_API_URL = "my_api_url";
    private static final String THIRD_PARTY_API_URL = "third_party_api_url";

    @Provides
    @Singleton
    Cache provideOkHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        return new Cache(application.getCacheDir(), cacheSize);
    }

    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient(Cache cache) {
        OkHttpClient client = new OkHttpClient();
        client.setCache(cache);
        return client;
    }

    @Provides
    @Singleton
    TypeAdapter<MyClass> provideMyAPITypeAdapter() {
        return new TypeAdapter<MyClass>() {
            // implementation ignored
        };
    }

    @Provides
    @Named("myApiGson")
    Gson provideGsonForMyAPI(TypeAdapter<MyClass> adapter) {
        return new GsonBuilder()
                .registerTypeAdapter(MyClass.class, adapter)
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .create();
    }

    @Provides
    @Named("thirdPartyApiGson")
    Gson provideGsonForThirdPartyAPI() {
        return new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .create();
    }

    @Provides
    @Named("myApiRestAdapter")
    RestAdapter provideMyRestAdapter(Gson gson, OkHttpClient okHttpClient) {
       return new RestAdapter.Builder()
                .setEndpoint(MY_API_URL)
                .setConverter(new GsonConverter(gson))
                .setClient(new OkClient(okHttpClient))
                .build();
    }

    @Provides
    @Named("thirdPartyApiRestAdapter")
    RestAdapter provideThirdPartyRestAdapter(Gson gson, OkHttpClient okHttpClient) {
       return new RestAdapter.Builder()
                .setEndpoint(THIRD_PARTY_API_URL)
                .setConverter(new GsonConverter(gson))
                .setClient(new OkClient(okHttpClient))
                .build();
    }

    @Provides
    @Singleton
    MyAPI provideMyAPI(RestAdapter adapter){
        return adapter.create(MyAPI.class);
    }

    @Provides
    @Singleton
    ThirdPartyAPI provideThirdPartyAPI(RestAdapter adapter){
        return adapter.create(ThirdPartyAPI.class);
    }
}

Comme vous pouvez le voir ci-dessus dans le code, le NetModule dispose de méthodes pour renvoyer deux objets Gson et deux objets RestAdapter. Mes questions sont;

  1. Comment puis-je m'assurer que les dépendances correctes sont injectées lors de la création d'interfaces RestAdapter et API spécifiques? (provideMyRestAdapter() requiert GSON renvoyé de provideGsonForMyAPI() et provideMyAPI() nécessite RestAdapter renvoyé de provideMyRestAdapter().)

  2. Comment puis-je m'assurer que seules deux instances de RestAdapter (une pour mon API et une autre pour une API tierce) sont créées au cours de la vie de l'application, car la création de RestAdapter est considérée comme coûteuse? J'utilise l'attribut @Named sur les méthodes renvoyant RestAdapters. Dites par exemple lorsque vous injectez une dépendance directement dans un champ comme ceci: @Inject("myApiRestAdapter") RestAdapter myRestadapter; Est-ce que Dagger 2 va créer un nouveau RestAdapter à chaque fois ou utilisera-t-il un créé auparavant (comme @Singleton mais pour un objet spécifique)?

Je viens de commencer à utiliser Dagger 2 et ma compréhension de son utilisation peut encore être incorrecte. S'il vous plaît corrigez-moi si je fais quelque chose de mal ici. Merci de supporter cette longue question. 

16
Much Overflow

Vous êtes déjà à mi-chemin de la solution. Pour compléter la solution, essayez ce qui suit:

@Provides
@Named("myApiRestAdapter")
RestAdapter provideMyRestAdapter(@Named("myApiGson") Gson gson, OkHttpClient okHttpClient) {
   return new RestAdapter.Builder()
            .setEndpoint(MY_API_URL)
            .setConverter(new GsonConverter(gson))
            .setClient(new OkClient(okHttpClient))
            .build();
}

@Provides
@Named("thirdPartyApiRestAdapter")
RestAdapter provideThirdPartyRestAdapter(@Named("thirdPartyApiGson") Gson gson, OkHttpClient okHttpClient) {
   return new RestAdapter.Builder()
            .setEndpoint(THIRD_PARTY_API_URL)
            .setConverter(new GsonConverter(gson))
            .setClient(new OkClient(okHttpClient))
            .build();
}

Pour vous assurer que seules deux instances de vos RestAdapters sont créées pendant la durée de vie de l'application, annotez les deux méthodes fournissant RestAdapter avec @Singleton comme vous l'avez fait avec vos autres méthodes. En ce qui concerne votre autre question, à savoir si Dagger 2 créera une nouvelle instance de RestAdapter chaque fois qu’il devra l’injecter, je pense qu’il le fait exactement, mais je n’en suis pas sûr.

J'espère que cela t'aides!

26
Harry

J'ai vu ce fil après avoir posté ma réponse à une question similaire. Je voulais fournir un lien car je pense que la même approche pourrait être utile selon votre situation. C'est peut-être exagéré pour cette question précise, mais je voulais le partager au cas où cela aiderait quelqu'un d'autre.

https://stackoverflow.com/a/52348744/5046784

En bref, vous pouvez créer des interfaces/classes uniques pour chaque objet nommé (par exemple, MyApiGson et ThirdPartyApiGson), puis créer @Provides pour ces objets plutôt que la classe Gson générique. De cette façon, vous pouvez injecter les instances par classe/interface plutôt que par un nom de chaîne magique que vous devez rechercher ou mémoriser. C'est un peu plus de travail, mais cela aide lorsque vous avez plusieurs modules indépendants qui fournissent différentes instances de la même classe.

0
Justin Fiedler