Anrufadapter für Klassenbeispiel kann nicht erstellt werden. Einfach


86

Ich verwende Retrofit 2.0.0-Beta1 mit SimpleXml. Ich möchte eine einfache (XML) Ressource von einem REST-Service abrufen. Das Marshalling / Unmarshalling des Simple-Objekts mit SimpleXML funktioniert einwandfrei.

Bei Verwendung dieses Codes (konvertierter Code vor 2.0.0):

final Retrofit rest = new Retrofit.Builder()
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .baseUrl(endpoint)
    .build();
SimpleService service = rest.create(SimpleService.class);
LOG.info(service.getSimple("572642"));

Bedienung:

public interface SimpleService {

    @GET("/simple/{id}")
    Simple getSimple(@Path("id") String id);

}

Ich bekomme diese Ausnahme:

Exception in thread "main" java.lang.IllegalArgumentException: Unable to create call adapter for class example.Simple
    for method SimpleService.getSimple
    at retrofit.Utils.methodError(Utils.java:201)
    at retrofit.MethodHandler.createCallAdapter(MethodHandler.java:51)
    at retrofit.MethodHandler.create(MethodHandler.java:30)
    at retrofit.Retrofit.loadMethodHandler(Retrofit.java:138)
    at retrofit.Retrofit$1.invoke(Retrofit.java:127)
    at com.sun.proxy.$Proxy0.getSimple(Unknown Source)

Was vermisse ich? Ich weiß, dass das Umschließen des Rückgabetyps mit einem Callfunktioniert. Ich möchte jedoch, dass der Dienst Geschäftsobjekte als Typ zurückgibt (und im Synchronisierungsmodus arbeitet).

AKTUALISIEREN

Nach dem Hinzufügen der zusätzlichen Abhängigkeiten und .addCallAdapterFactory(RxJavaCallAdapterFactory.create())wie durch verschiedene Antworten vorgeschlagen, wird immer noch der folgende Fehler angezeigt:

Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for class simple.Simple. Tried:
 * retrofit.RxJavaCallAdapterFactory
 * retrofit.DefaultCallAdapter$1


Für diejenigen, die Coroutine verwenden, überprüfen Sie @chatlanin Antwort
NJ

Antworten:


66

Kurze Antwort: Rückkehr Call<Simple> in Ihre Serviceschnittstelle zurück.

Es sieht so aus, als würde Retrofit 2.0 versuchen, das Proxy-Objekt für Ihre Serviceschnittstelle zu erstellen. Es erwartet, dass Sie dies schreiben:

public interface SimpleService {
    @GET("/simple/{id}")
    Call<Simple> getSimple(@Path("id") String id);
}

Es möchte jedoch immer noch gut spielen und flexibel sein, wenn Sie a nicht zurückgeben möchten Call. Um dies zu unterstützen, hat es das Konzept einesCallAdapter , das wissen soll, wie man a Call<Simple>in a umwandelt Simple.

Die Verwendung von RxJavaCallAdapterFactory ist nur nützlich, wenn Sie versuchen, zurückzukehren rx.Observable<Simple>.

Die einfachste Lösung besteht darin, a Callwie von Retrofit erwartet zurückzugeben. Sie können auch eine schreiben, CallAdapter.Factorywenn Sie sie wirklich brauchen.


1
Ja, dies ist in der Tat die richtige Antwort :(. Ich weiß von Anfang an, dass Call<Simple>es so funktioniert. Wie in meiner Frage angegeben , ist es jedoch meine Präferenz, einfach zurückzukehren Simple(wie es in der früheren Version der Fall war). Meine Schlussfolgerung ist : nicht möglich ohne zusätzlichen Code.
Müller

Der von Ihnen angegebene Link Call<Simple> ist fehlerhaft. Zu welchem ​​Paket Simplegehört?
Shyam

Danke für den Hinweis @shyam. Ich habe die Links aktualisiert. Simpleist die in der OP-Frage erwähnte Klasse. Der Link ist zu Call<T>.
Hosam Aly

72

Im Fall von Kotlin und Coroutinen trat diese Situation auf, als ich vergaß, die API-Servicefunktion so zu markieren, suspendals würde ich diese Funktion aufrufen von CoroutineScope(Dispatchers.IO).launch{}:

Verwendung:

    val apiService = RetrofitFactory.makeRetrofitService()

    CoroutineScope(Dispatchers.IO).launch {

        val response = apiService.myGetRequest()

        // process response...

    }

ApiService.kt

interface ApiService {

       @GET("/my-get-request")
       suspend fun myGetRequest(): Response<String>
}

53

Abhängigkeiten hinzufügen:

compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta1'

Erstellen Sie Ihren Adapter folgendermaßen:

Retrofit rest = new Retrofit.Builder()
    .baseUrl(endpoint)
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .build();

addCallAdapterFactory ()und addConverterFactory ()beide müssen angerufen werden.

Bedienung:

public interface SimpleService {

    @GET("/simple/{id}")
    Call<Simple> getSimple(@Path("id") String id);

}

Ändern Simplein Call<Simple>.


7
Ich weiß, dass das Ändern von Simple to Call <Simple> den Trick macht. Ich möchte jedoch keinen Typ Callin die Serviceschnittstelle
einführen

Sie können auch CompletableFutureSee stackoverflow.com/questions/32269064/…
Johannes Barop

38

Mit der neuen Nachrüstung (2. +) müssen Sie addCallAdapterFactory hinzufügen, die eine normale oder eine RxJavaCallAdapterFactory (für Observables) sein kann. Ich denke, Sie können auch mehr als beides hinzufügen. Es wird automatisch überprüft, welches verwendet werden soll. Siehe ein Arbeitsbeispiel unten. Sie können diesen Link auch für weitere Details überprüfen .

 Retrofit retrofit = new Retrofit.Builder().baseUrl(ApiConfig.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build()

1
Toller Link! Wird später überprüfen.
Müller

1
In der Dokumentation gibt es noch RestAdapter und es gibt kein Beispiel dafür, wie man mit Retrofit 2.0 die einfachste Anfrage stellt. Ich erhalte die Fehlermeldung RxJavaCallAdapterFactory konnte nicht aufgelöst werden. Verbrachte einige Stunden, um die einfachste Android-http-Anfrage zu stellen. Es scheint nicht so einfach zu sein, wie sie werben. Vielleicht sollten sie etwas mehr dokumentieren.
Vlado Pandžić

Verwenden Sie Retrofit anstelle von RestAdapter, wenn Sie Retrofit 2.0 verwenden.
Himanshu Virmani

5
Für RxJavaCallAdapterFactory ist das Adapter-rxjava-Artefakt aus demselben Repo erforderlich. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl

3
Stellen Sie für Retrofit2 sicher, dass Sie die richtigen Abhängigkeiten verwenden com.squareup.retrofit2:adapter-rxjava:2.1.0und com.squareup.retrofit2:converter-gson:2.1.0
George Papas

16

Wenn Sie verwenden möchten retrofit2und nicht immer zurückkehren möchten retrofit2.Call<T>, müssen Sie einen eigenen erstellen, der den CallAdapter.Factoryerwarteten einfachen Typ zurückgibt. Der einfache Code kann folgendermaßen aussehen:

import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

public class SynchronousCallAdapterFactory extends CallAdapter.Factory {
    public static CallAdapter.Factory create() {
        return new SynchronousCallAdapterFactory();
    }

    @Override
    public CallAdapter<Object, Object> get(final Type returnType, Annotation[] annotations, Retrofit retrofit) {
        // if returnType is retrofit2.Call, do nothing
        if (returnType.toString().contains("retrofit2.Call")) {
            return null;
        }

        return new CallAdapter<Object, Object>() {
            @Override
            public Type responseType() {
                return returnType;
            }

            @Override
            public Object adapt(Call<Object> call) {
                try {
                    return call.execute().body();
                } catch (Exception e) {
                    throw new RuntimeException(e); // do something better
                }
            }
        };
    }
}

Dann registrieren Sie einfach das SynchronousCallAdapterFactoryin Retrofit, um Ihr Problem zu lösen.

Retrofit rest = new Retrofit.Builder()
        .baseUrl(endpoint)
        .addConverterFactory(SimpleXmlConverterFactory.create())
        .addCallAdapterFactory(SynchronousCallAdapterFactory.create())
        .build();

Danach können Sie einfachen Typ ohne zurückgeben retrofit2.Call.

public interface SimpleService {
    @GET("/simple/{id}")
    Simple getSimple(@Path("id") String id);
}

Super danke. Wissen Sie, ob es eine Möglichkeit gibt, einen asynchronen Anrufadapter zu erstellen? so kann ich einen Dienst wie diesen aufrufen: service.getSimple ("id", neuer Adapter () {onFail () {} onSuccess () {}}, anstatt die enqueue () und den Rückruf zu verwenden?
markov00

"implementiert CallAdapter.Factory" Dies ist eine Klasse, keine Schnittstelle?
Ozmank

@ozmank In der Version war 2.0.0-beta3es eine Schnittstelle, jetzt in der Version ist 2.1.0es eine Klasse. Ich habe meinen Code bearbeitet, um aktueller zu sein. Vielen Dank.
Mateusz Korwel

@MateuszKorwel Es wird immer eine neue RuntimeException (e) ausgelöst! Ich möchte String anstelle von Simple zurückgeben.
Dr.jacky

3
Vielen Dank, dass Sie dies gepostet haben. Ich habe eine Bibliothek um diese Behandlung Simple getSimple()und Response<Simple> getSimple()Anforderungen erstellt: github.com/jaredsburrows/retrofit2-synchronous-adapter .
Jared Burrows

13

Fügen Sie die folgenden Abhängigkeiten für die Nachrüstung 2 hinzu

 compile 'com.squareup.retrofit2:retrofit:2.1.0'

für GSON

 compile 'com.squareup.retrofit2:converter-gson:2.1.0'

für Observablen

compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

In Ihrem Fall für XML müssten Sie die folgenden Abhängigkeiten angeben

 compile 'com.squareup.retrofit2:converter-simplexml:2.1.0'

Aktualisieren Sie den Serviceabruf wie folgt

final Retrofit rest = new Retrofit.Builder()
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .baseUrl(endpoint)
    .build();
SimpleService service = rest.create(SimpleService.class);

Dies ist das gleiche wie mein Setup.
Müller

1
Stellen Sie außerdem sicher, dass Sie diese Zeile beim Erstellen des Adapters hinzufügen: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan

Fehler beim Auflösen: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank

1
@ozmank seine Nachrüstung2, ich habe meine Antwort auch aktualisiert. Bitte probieren Sie es aus
rahulrv

4

in meinem Fall mit diesem

compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

mit diesem

new Retrofit.Builder().addCallAdapterFactory(RxJava2CallAdapterFactory.create())...

löste das Problem, als nichts anderes funktionierte


2

Wenn Sie RxJava nicht verwenden, ist es nicht sinnvoll, RxJava nur zur Nachrüstung hinzuzufügen. 2.5.0unterstützt CompletableFutureintegrierte Funktionen, die Sie verwenden können, ohne eine andere Bibliothek oder einen anderen Adapter hinzuzufügen.

build.gradle.kts

implementation("com.squareup.retrofit2:retrofit:2.5.0")
implementation("com.squareup.retrofit2:retrofit-converters:2.5.0")

Api.kt

interface Api {
    @GET("/resource")
    fun listCompanies(): CompletableFuture<ResourceDto>
}

Verwendung:

Retrofit.Builder()
   .addConverterFactory(SimpleXmlConverterFactory.create())
   .baseUrl("https://api.example.com")
   .build()
   .create(Api::class.java)

2

IllegalArgumentException: Aufrufadapter für Klasse java.lang.Object kann nicht erstellt werden

Kurze Antwort: Ich habe es durch die folgenden Änderungen gelöst

ext.retrofit2Version = '2.4.0' -> '2.6.0'
implementation"com.squareup.retrofit2:retrofit:$retrofit2Version"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit2Version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit2Version"

Viel Glück


1

Nur um die Anrufbeispiele für Personen zu verdeutlichen, die migrieren, keinen Empfang verwenden oder synchrone Anrufe wünschen - Anruf ersetzt im Wesentlichen die Antwort (Wraps), dh:

Response<MyObject> createRecord(...);

wird

Call<MyObject> createRecord(...);

und nicht

Call<Response<MyObject>> createRecord(...);

(für die noch ein Adapter erforderlich ist)


Mit dem Anruf können Sie dann weiterhin verwenden, isSuccessfulda tatsächlich eine Antwort zurückgegeben wird. Sie können also Folgendes tun:

myApi.createRecord(...).execute().isSuccessful()

Oder greifen Sie auf Ihren Typ (MyObject) zu:

MyObject myObj = myApi.createRecord(...).execute().body();

1
public interface SimpleService {

  @GET("/simple/{id}")
  Simple getSimple(@Path("id") String id);

}

Die Kommunikation mit dem Netzwerk erfolgt über den separaten Thread, daher sollten Sie Ihr Simple damit ändern.

public interface SimpleService {

  @GET("/simple/{id}")
  Call<Simple> getSimple(@Path("id") String id);

}

1

In meinem Fall habe ich verwendet

com.squareup.retrofit2:adapter-rxjava:2.5.0 //notice rxjava

anstatt

com.squareup.retrofit2:adapter-rxjava2:2.5.0 //notice rxjava2

Sie sollten bei der Verwendung com.squareup.retrofit2:adapter-rxjava2:2.5.0verwendenio.reactivex.rxjava2


0

Sie können einen Rückruf implementieren und die Funktion Einfach von onResponse abrufen.

public class MainActivity extends Activity implements Callback<Simple> {

    protected void onCreate(Bundle savedInstanceState) {
        final Retrofit rest = new Retrofit.Builder()
                    .addConverterFactory(SimpleXmlConverterFactory.create())
                    .baseUrl(endpoint)
                    .build();
        SimpleService service = rest.create(SimpleService.class);
        Call<Simple> call = service.getSimple("572642");
        //asynchronous call
        call.enqueue(this);

        return true;
    }

    @Override
    public void onResponse(Response<Simple> response, Retrofit retrofit) {
       // response.body() has the return object(s)
    }

    @Override
    public void onFailure(Throwable t) {
        // do something
    }

}

-2

Die Art und Weise, wie ich dieses Problem behoben habe, bestand darin, dies zur Anwendungsgradle-Datei hinzuzufügen. Wenn die Konfiguration nicht festgelegt ist, kommt es zu Konflikten. Möglicherweise wird dies in der stabilen Version der Bibliothek behoben:

configurations {
    compile.exclude group: 'stax'
    compile.exclude group: 'xpp3'
}

dependencies {
    compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
    compile 'com.squareup.retrofit:converter-simplexml:2.0.0-beta1'
}

und erstellen Sie Ihren Adapter folgendermaßen:

  Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(endPoint)
            .addConverterFactory(SimpleXmlConverterFactory.create())
            .build();

Ich hoffe es hilft!


1
Dies ist genau die gleiche Konfiguration wie in meiner Frage. Also, löst nichts auf :)
rmuller
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.