Impossibile creare l'adattatore di chiamata per l'esempio di classe


91

Sto usando il retrofit 2.0.0-beta1 con SimpleXml. Desidero recuperare una risorsa semplice (XML) da un servizio REST. Marshalling / Unmarshalling dell'oggetto Simple con SimpleXML funziona bene.

Quando si utilizza questo codice (modulo convertito prima del codice 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"));

Servizio:

public interface SimpleService {

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

}

Ottengo questa eccezione:

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)

Cosa mi manca? So che il wrapping del tipo restituito da un Callfunziona. Ma voglio che il servizio restituisca gli oggetti business come tipo (e funzioni in modalità di sincronizzazione).

AGGIORNARE

Dopo aver aggiunto le dipendenze extra e .addCallAdapterFactory(RxJavaCallAdapterFactory.create())come suggerito da diverse risposte, ricevo ancora questo errore:

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


1
Per coloro che utilizzano Coroutine, controllare la risposta @chatlanin
NJ

Risposte:


68

Risposta breve: torna Call<Simple>nell'interfaccia del servizio.

Sembra che Retrofit 2.0 stia cercando di trovare un modo per creare l'oggetto proxy per l'interfaccia del servizio. Si aspetta che tu scriva questo:

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

Tuttavia, vuole comunque giocare bene ed essere flessibile quando non vuoi restituire un file Call. A supporto di ciò, ha il concetto di a CallAdapter, che dovrebbe sapere come adattare a Call<Simple>in a Simple.

L'uso di RxJavaCallAdapterFactoryè utile solo se stai cercando di tornare rx.Observable<Simple>.

La soluzione più semplice è restituire un Callcome si aspetta Retrofit. Puoi anche scrivere un messaggio CallAdapter.Factoryse ne hai davvero bisogno.


1
Sì, questa è davvero la risposta giusta :(. So fin dall'inizio che Call<Simple>è così che funziona. Tuttavia, come affermato nella mia domanda, la mia preferenza è quella di tornare Simple(come nella versione precedente). La mia conclusione è : non possibile senza codice aggiuntivo
rmuller

Il collegamento che hai fornito Call<Simple> è interrotto. A quale pacchetto Simpleappartiene?
timido

Grazie per la nota @shyam. Ho aggiornato i link. Simpleè la classe menzionata nella domanda dell'OP. Il collegamento è a Call<T>.
Hosam Aly

94

Nel caso di Kotlin e coroutines questa situazione si è verificata quando ho dimenticato di contrassegnare la funzione di servizio API come suspendquando chiamo questa funzione da CoroutineScope(Dispatchers.IO).launch{}:

Utilizzo:

    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>
}

Hai salvato il mio fratello vivo s2
Guilherme Ramos

durante l'utilizzo di Coroutines, dobbiamo utilizzare la sospensione con la tua funzione. che ha aiutato grazie amico
Abubakar Rafi

ha funzionato per me tnx. l'aggiunta della sospensione è la soluzione a questo errore
mmdreza baqalpour

53

aggiungi dipendenze:

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'

crea il tuo adattatore in questo modo:

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

addCallAdapterFactory ()ed addConverterFactory ()entrambi devono essere chiamati.

Servizio:

public interface SimpleService {

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

}

Modifica Simplein Call<Simple>.


7
So che cambiare Simple to Call <Simple> fa il trucco. Tuttavia, non voglio introdurre il tipo nell'interfaccia Calldel servizio
rmuller

Puoi anche utilizzare CompletableFutureVedi stackoverflow.com/questions/32269064/…
Johannes Barop

38

Con il nuovo Retrofit (2. +) è necessario aggiungere addCallAdapterFactory che può essere normale o RxJavaCallAdapterFactory (per Observables). Penso che tu possa aggiungere anche più di entrambi. Controlla automaticamente quale usare. Vedere un esempio funzionante di seguito. Puoi anche controllare questo link per maggiori dettagli.

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

1
Ottimo collegamento! Controllerò più tardi.
rmuller

1
Nella documentazione c'è ancora RestAdapter e non c'è nessun esempio su come fare la richiesta più semplice con Retrofit 2.0. Ricevo un errore Impossibile risolvere RxJavaCallAdapterFactory. Ho trascorso alcune ore per rendere la richiesta http Android più semplice. Sembra che non sia così semplice come pubblicizzano. Forse dovrebbero documentare un po 'di più.
Vlado Pandžić

Usa Retrofit invece di RestAdapter se stai usando Retrofit 2.0.
Himanshu Virmani

5
RxJavaCallAdapterFactory richiede l'artefatto adapter-rxjava dallo stesso repository. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl

3
per retrofit2, assicurati di utilizzare le dipendenze corrette com.squareup.retrofit2:adapter-rxjava:2.1.0e com.squareup.retrofit2:converter-gson:2.1.0
George Papas

16

Se vuoi usarlo retrofit2e non vuoi tornare sempre retrofit2.Call<T>, devi crearne uno tuo CallAdapter.Factoryche restituisca il tipo semplice come previsto. Il codice semplice può assomigliare a questo:

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

Quindi la semplice registrazione SynchronousCallAdapterFactoryin Retrofit dovrebbe risolvere il tuo problema.

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

Dopodiché puoi restituire il tipo semplice senza retrofit2.Call.

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

Fantastico grazie. Sai se esiste un modo per creare un adattatore di chiamata asincrono? quindi posso chiamare un servizio come questo: service.getSimple ("id", new Adapter () {onFail () {} onSuccess () {}} invece di usare enqueue () e callback?
markov00

"implementa CallAdapter.Factory" Questa è una classe non un'interfaccia?
ozmank

@ozmank Nella versione 2.0.0-beta3era un'interfaccia, ora nella versione 2.1.0è una classe. Ho modificato il mio codice per essere più attuale. Grazie.
Mateusz Korwel

@MateuszKorwel Ottiene sempre una nuova RuntimeException (e)! Voglio restituire String invece di Simple.
Dr.jacky

3
Grazie per aver postato questo. Ho creato una libreria attorno a questa gestione Simple getSimple()e Response<Simple> getSimple()richieste: github.com/jaredsburrows/retrofit2-synchronous-adapter .
Jared Burrows

13

Aggiungere le seguenti dipendenze per il retrofit 2

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

per GSON

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

per osservabili

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

Nel tuo caso per XML, dovresti includere le seguenti dipendenze

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

Aggiorna la chiamata di servizio come di seguito

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

Questo è lo stesso del mio setup.
rmuller

1
assicurati anche di aggiungere questa riga durante la creazione dell'adattatore: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan

Impossibile risolvere: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank

1
@ozmank è retrofit2, ho aggiornato anche la mia risposta. Si prega di provarlo
rahulrv

5

IllegalArgumentException: impossibile creare l'adattatore di chiamata per la classe java.lang.Object

Risposta breve: l'ho risolto con le seguenti modifiche

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"

In bocca al lupo


4

nel mio caso usando questo

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

con questo

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

risolto il problema quando nient'altro funzionava


2

Se non stai usando RxJava, non ha senso aggiungere RxJava solo per il retrofit. 2.5.0ha il supporto per CompletableFuturebuilt in che puoi usare senza aggiungere altre librerie o adattatori.

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>
}

Utilizzo:

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

1

Solo per rendere gli esempi di chiamata più chiari per le persone che stanno migrando, non utilizzano Rx o che desiderano chiamate sincrone - Call essenzialmente sostituisce (avvolge) la risposta, che significa:

Response<MyObject> createRecord(...);

diventa

Call<MyObject> createRecord(...);

e non

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

(che richiederà comunque un adattatore)


La chiamata ti consentirà quindi di utilizzarla ancora isSuccessfulpoiché restituisce effettivamente una risposta. Quindi puoi fare qualcosa come:

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

Oppure accedi al tuo Tipo (MyObject) come:

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

1
public interface SimpleService {

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

}

La comunicazione con la rete avviene con il thread separato, quindi dovresti cambiare il tuo Simple con quello.

public interface SimpleService {

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

}

1

Nel mio caso ho usato

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

invece di

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

dovresti usare com.squareup.retrofit2:adapter-rxjava2:2.5.0quando usiio.reactivex.rxjava2


0

È possibile implementare un callback, ottenere la funzione Simple from onResponse.

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

Il modo in cui ho risolto questo problema è stato aggiungerlo al file gradle dell'applicazione, se la configurazione non è impostata ci saranno conflitti, forse questo verrà risolto nella versione stabile della libreria:

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'
}

e crea il tuo adattatore in questo modo:

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

Spero che sia d'aiuto!


1
Questa è esattamente la stessa configurazione della mia domanda. Quindi, non risolve nulla :)
rmuller
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.