webentwicklung-frage-antwort-db.com.de

Dolch 2 fügt mehrere Instanzen desselben Objekttyps ein

Hintergrund

Ich konvertiere meine App zur MVP-Architektur und habe festgestellt, dass Dagger 2 nützlich ist, um bei Bedarf Abhängigkeiten einzufügen. Meine App muss mit zwei Web-APIs (meiner eigenen und einer Drittanbieter-API) kommunizieren. Es kann vorkommen, dass Anfragen an meine eigene API und die Drittanbieter-API gleichzeitig ausgelöst werden. Ich benutze Retrofit, um mit diesen Apis zu kommunizieren, und benutze GSON für die Serialisierung/Deserialisierung.

Was ich vorher gemacht habe

Ich habe zwei Retrofit RestAdapters erstellt und das Service Locator-Muster verwendet, um sie bei Bedarf zu erhalten. Der RestAdapter, der für meine eigene API gedacht ist, enthält GSONConverter mit einigen benutzerdefinierten TypeAdaptern, da ich keine 1: 1-JSON-Deserialisierung meiner Antwort in der App möchte. Der andere RestAdapter ist für die API eines Drittanbieters vorgesehen und verwendet einen anderen GSONConverter mit einer bestimmten Feldbenennungsrichtlinie.

Problem

Ich versuche, DI anstelle von Service Locator zu verwenden, um meinen RestAdapter (und die API-Schnittstelle) zu erhalten. Ich habe meine NetModule-Klasse wie folgt eingerichtet

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

Wie Sie oben im Code sehen können, verfügt das NetModule über Methoden, um zwei Gson-Objekte und zwei RestAdapter-Objekte zurückzugeben. Meine Fragen sind:

  1. Wie stelle ich sicher, dass beim Erstellen bestimmter RestAdapter- und API-Schnittstellen die richtigen Abhängigkeiten eingefügt werden? (provideMyRestAdapter() erfordert, dass GSON von provideGsonForMyAPI() zurückgegeben wird, und provideMyAPI() erfordert, dass RestAdapter von provideMyRestAdapter() zurückgegeben wird.)

  2. Wie kann ich sicherstellen, dass nur zwei Instanzen von RestAdapter (eine für meine API und andere für Drittanbieter-API) während der gesamten Lebensdauer der Anwendung erstellt werden, da das Erstellen von RestAdapter als teuer gilt. Ich verwende @Named-Attribut für Methoden, die RestAdapters zurückgeben. Sagen Sie zum Beispiel, wenn Sie die Abhängigkeit direkt in ein Feld wie folgt einfügen: @Inject("myApiRestAdapter") RestAdapter myRestadapter; Wird Dagger 2 jedes Mal einen neuen RestAdapter erstellen oder wird er einen zuvor erstellten verwenden (wie @Singleton, aber für ein bestimmtes Objekt)?

Ich habe gerade angefangen, Dagger 2 zu verwenden, und mein Verständnis der Verwendung von Dagger 2 ist möglicherweise immer noch falsch. Bitte korrigieren Sie mich, wenn ich hier etwas falsch mache. Vielen Dank, dass Sie diese lange Frage beantwortet haben. 

16
Much Overflow

Sie sind bereits zur Hälfte mit der Lösung fertig. Um die Lösung abzuschließen, versuchen Sie Folgendes:

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

Um sicherzustellen, dass nur zwei Instanzen Ihrer RestAdapters während der Lebensdauer der Anwendung erstellt werden, kommentieren Sie beide Methoden, die RestAdapter mit @Singleton versehen, wie Sie es mit Ihren anderen Methoden getan haben. Was Ihre andere Frage betrifft, ob Dagger 2 jedes Mal eine neue Instanz von RestAdapter erstellt, wenn es injiziert wird, denke ich, dass dies genau der Fall ist, aber ich bin mir nicht sicher.

Hoffe das hilft!

26
Harry

Ich habe diesen Thread gesehen, nachdem ich meine Antwort auf eine ähnliche Frage gepostet hatte. Ich wollte einen Link bereitstellen, weil ich denke, dass derselbe Ansatz abhängig von Ihrer Situation nützlich sein kann. Für diese genaue Frage mag es übertrieben sein, aber ich wollte mit Ihnen teilen, falls sie jemandem hilft.

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

Kurz gesagt, Sie können eindeutige Schnittstellen/Klassen für jedes benannte Objekt erstellen (z. B. MyApiGson und ThirdPartyApiGson) und dann @Provides für diese und nicht für die generische Gson-Klasse erstellen. Auf diese Weise können Sie die Instanzen nach Klasse/Schnittstelle einfügen, anstatt nach dem Namen einer magischen Zeichenfolge, die Sie nachschlagen oder sich merken müssen. Es ist ein bisschen mehr Arbeit, aber es hilft, wenn Sie eine Reihe unabhängiger Module haben, die unterschiedliche Instanzen derselben Klasse bereitstellen.

0
Justin Fiedler