Pytanie Sztylet 2 wstrzykuje wiele instancji tego samego typu obiektu


tło

Konwertuję swoją aplikację na architekturę MVP i znalazłem Dagger 2, który jest użyteczny do wstrzykiwania zależności w razie potrzeby. Moja aplikacja musi komunikować się z dwoma aplikacjami internetowymi (moim i api strony trzeciej). Może się zdarzyć, że żądania do mojego api i api strony trzeciej będą mogły być uruchamiane w tym samym czasie. Używam Retrofitu do komunikowania się z tymi apisami i używania GSON do szeregowania / deserializacji.

Co wcześniej zrobiłem

Stworzyłem dwa elementy Restartit RestAdapters i zastosowałem wzór Service Locator, aby uzyskać je w razie potrzeby. RestAdapter, który ma być używany dla mojego własnego interfejsu API, zawiera program GSONConverter z niektórymi niestandardowymi TypeAdapters, ponieważ nie chcę odmiany JSON 1: 1 mojej odpowiedzi w aplikacji. Drugi RestAdapter przeznaczony do api innej firmy i używa innego narzędzia GSONConverter z określoną polityką nazewnictwa pól.

Problem

Próbuję użyć DI zamiast Service Locator, aby uzyskać mój RestAdapter (i interfejs API). Mam swoją konfigurację klasy NetModule w następujący sposób

@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);
    }
}

Jak widać powyżej w kodzie, moduł NetModule ma metody zwracania dwóch obiektów Gson i dwóch obiektów RestAdapter. Moje pytania są;

  1. W jaki sposób upewnić się, że podczas tworzenia konkretnych interfejsów ADR i interfejsów API wstrzykiwane są właściwe zależności? (provideMyRestAdapter() wymaga zwrotu GSON provideGsonForMyAPI() i provideMyAPI() wymaga zwrotu RestAdapter z provideMyRestAdapter().)

  2. Jak mogę się upewnić, że tylko dwa wystąpienia RestAdapter (Jeden dla mojego api i innych dla api stron trzecich) są kiedykolwiek tworzone w czasie życia aplikacji, ponieważ tworzenie RestAdaptera jest uważane za drogie. ja używam @Named atrybut dla metod zwracających RestaAdapters. Powiedz na przykład, kiedy wstrzykujesz zależność bezpośrednio do tego pola: @Inject("myApiRestAdapter") RestAdapter myRestadapter; czy Dagger 2 będzie za każdym razem tworzył nowy RestAdapter, czy też użyje utworzonego wcześniej (np @Singleton ale dla konkretnego obiektu)?

Właśnie zacząłem używać Dagger 2 i moje zrozumienie tego, jak z niego korzystać, może być nadal nieprawidłowe. Proszę mnie poprawić, jeśli robię coś złego tutaj. Dziękuję za tak długie pytanie.


14
2018-03-09 05:31


pochodzenie




Odpowiedzi:


Jesteś już w połowie rozwiązania. Aby zakończyć rozwiązanie, spróbuj wykonać następujące czynności:

@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();
}

Aby upewnić się, że tylko dwa wystąpienia Twoich urządzeń RestAdapter są tworzone podczas całego okresu użytkowania aplikacji, należy zanotować obie metody dostarczające RestAdapter z @Singleton tak jak zrobiłeś z innymi metodami. Jeśli chodzi o twoje drugie pytanie, czy Dagger 2 stworzy nową instancję RestAdaptera za każdym razem, gdy będzie musiał wstrzyknąć, myślę, że robi to dokładnie, ale nie jestem tego pewien.

Mam nadzieję że to pomoże!


23
2018-03-09 05:57



Dzięki @pratt Spróbuję. Mam jedno pytanie, ale nie @Singleton ma utworzyć tylko jeden obiekt dla danego typu klasy? W tym przypadku Jeśli dodaję adnotację do obu moich kart odpoczynku jako @Singleton (który jest typu RestAdapter) co stanie się za kulisami? - Much Overflow
Oprócz adnotowania Twojej metody jako @Singleton, ty też ją opatrujesz adnotacją @Named adnotacja, która powie sztyletowi, aby utworzyły dwa odrębne instancje RestAdapter dla każdej nazwy. Pamiętaj, aby wskazać, który RestAdapter potrzebujesz, używając @Named jak pokazałem w powyższej odpowiedzi. - Harry
To ma sens, dzięki. Spróbuję tego dziś wieczorem i wrócę do ciebie! - Much Overflow