Impossible de créer un adaptateur d'appel pour l'exemple de classe.


87

J'utilise la retrofit 2.0.0-beta1 avec SimpleXml. Je veux récupérer une ressource simple (XML) à partir d'un service REST. Marshalling / Unmarshalling l'objet Simple avec SimpleXML fonctionne très bien.

Lors de l'utilisation de ce code (format converti avant le code 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"));

Un service:

public interface SimpleService {

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

}

J'obtiens cette exception:

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)

Qu'est-ce que je rate? Je sais que envelopper le type de retour par un Callfonctionne. Mais je souhaite que le service renvoie les objets métier sous forme de type (et fonctionne en mode synchro).

MISE À JOUR

Après avoir ajouté les dépendances supplémentaires et .addCallAdapterFactory(RxJavaCallAdapterFactory.create())comme suggéré par différentes réponses, j'obtiens toujours cette erreur:

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


Pour ceux qui utilisent Coroutine, vérifiez la réponse @chatlanin
NJ

Réponses:


66

Réponse courte: retournez Call<Simple>dans votre interface de service.

Il semble que Retrofit 2.0 essaie de trouver un moyen de créer l'objet proxy pour votre interface de service. Il attend de vous que vous écriviez ceci:

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

Cependant, il veut toujours jouer gentiment et être flexible lorsque vous ne voulez pas retourner un fichier Call. Pour supporter cela, il a le concept de a CallAdapter, qui est censé savoir comment adapter a Call<Simple>en a Simple.

L'utilisation de RxJavaCallAdapterFactoryn'est utile que si vous essayez de revenir rx.Observable<Simple>.

La solution la plus simple consiste à renvoyer un Calltel que prévu par Retrofit. Vous pouvez également écrire un CallAdapter.Factorysi vous en avez vraiment besoin.


1
Oui, c'est en effet la bonne réponse: (. Je sais depuis le début que Call<Simple>c'est ainsi que cela fonctionne. Cependant, comme indiqué dans ma question, ma préférence est de simplement revenir Simple(comme c'était le cas dans la version précédente). Ma conclusion est : pas possible sans code supplémentaire.
rmuller

Le lien que vous avez fourni Call<Simple> est rompu. À quel paquet Simpleappartient-il?
shyam

Merci pour la note @shyam. J'ai mis à jour les liens. Simpleest la classe mentionnée dans la question du PO. Le lien est à Call<T>.
Hosam Aly

75

Dans le cas de Kotlin et des coroutines, cette situation s'est produite lorsque j'ai oublié de marquer la fonction de service api comme suspendlorsque j'appelle cette fonction depuis CoroutineScope(Dispatchers.IO).launch{}:

Usage:

    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

ajouter des dépendances:

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'

créez votre adaptateur de cette façon:

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

addCallAdapterFactory ()et les addConverterFactory ()deux doivent être appelés.

Un service:

public interface SimpleService {

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

}

Modifiez Simpleen Call<Simple>.


7
Je sais que changer Simple to Call <Simple> fait l'affaire. Cependant, je ne veux pas introduire de type Calldans l'interface de service
rmuller

Vous pouvez également utiliser CompletableFutureVoir stackoverflow.com/questions/32269064/…
Johannes Barop

38

Avec le nouveau Retrofit (2. +), vous devez ajouter addCallAdapterFactory qui peut être normal ou RxJavaCallAdapterFactory (pour Observables). Je pense que vous pouvez ajouter plus que les deux. Il vérifie automatiquement lequel utiliser. Voir un exemple de travail ci-dessous. Vous pouvez également consulter ce lien pour plus de détails.

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

1
Excellent lien! Va vérifier plus tard.
rmuller

1
Dans la documentation, il y a toujours RestAdapter et il n'y a pas d'exemple pour faire la demande la plus simple avec Retrofit 2.0. J'obtiens une erreur Impossible de résoudre RxJavaCallAdapterFactory. J'ai passé quelques heures à faire la demande http Android la plus simple. Il semble que ce n’est pas aussi simple qu’ils le disent. Peut-être qu'ils devraient documenter un peu plus.
Vlado Pandžić

Utilisez Retrofit au lieu de RestAdapter si vous utilisez Retrofit 2.0.
Himanshu Virmani

5
RxJavaCallAdapterFactory requiert l'artefact adapter-rxjava du même référentiel. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl

3
pour retrofit2, assurez-vous d'utiliser les bonnes dépendances com.squareup.retrofit2:adapter-rxjava:2.1.0et com.squareup.retrofit2:converter-gson:2.1.0
George Papas

16

Si vous voulez utiliser retrofit2et que vous ne voulez pas toujours revenir retrofit2.Call<T>, vous devez créer le vôtre CallAdapter.Factoryqui retourne le type simple comme prévu. Le code simple peut ressembler à ceci:

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

Ensuite, enregistrez simplement le SynchronousCallAdapterFactorydans Retrofit devrait résoudre votre problème.

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

Après cela, vous pouvez retourner un type simple sans retrofit2.Call.

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

Génial merci. Savez-vous s'il existe un moyen de créer un adaptateur d'appel asynchrone? donc je peux appeler un service comme celui-ci: service.getSimple ("id", new Adapter () {onFail () {} onSuccess () {}} au lieu d'utiliser la mise en file d'attente () et le rappel?
markov00

"implements CallAdapter.Factory" C'est une classe pas une interface?
ozmank du

@ozmank Dans la version 2.0.0-beta3c'était une interface, maintenant dans la version 2.1.0c'est une classe. J'ai édité mon code, pour être plus actuel. Merci.
Mateusz Korwel le

@MateuszKorwel Il reçoit toujours une nouvelle RuntimeException (e)! Je veux retourner String au lieu de Simple.
Dr.jacky

3
Merci de poster ceci. J'ai créé une bibliothèque autour de cette manipulation Simple getSimple()et de ces Response<Simple> getSimple()requêtes: github.com/jaredsburrows/retrofit2-synchronous-adapter .
Jared Burrows

13

Ajoutez les dépendances suivantes pour la modernisation 2

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

pour GSON

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

pour les observables

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

Dans votre cas pour XML, vous devrez inclure les dépendances suivantes

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

Mettez à jour l'appel de service comme ci-dessous

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

C'est la même chose que ma configuration.
rmuller

1
assurez-vous également d'ajouter cette ligne lors de la création de l'adaptateur: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan

Échec de la résolution: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank

1
@ozmank sa retrofit2, j'ai également mis à jour ma réponse. Essayez-le
rahulrv

4

dans mon cas en utilisant ceci

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

avec ça

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

a résolu le problème alors que rien d'autre ne fonctionnait


2

Si vous n'utilisez pas RxJava, cela n'a aucun sens d'ajouter RxJava juste pour la modernisation. 2.5.0a un support pour CompletableFutureintégré que vous pouvez utiliser sans ajouter aucune autre bibliothèque ou adaptateur.

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

Usage:

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

2

IllegalArgumentException: impossible de créer un adaptateur d'appel pour la classe java.lang.Object

Réponse courte: je l'ai résolu par les changements suivants

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"

Bonne chance


1

Juste pour rendre les exemples d'appel plus clairs pour les personnes qui migrent, n'utilisent pas Rx ou qui veulent des appels synchrones - L'appel remplace essentiellement (encapsule) la réponse, ce qui signifie:

Response<MyObject> createRecord(...);

devient

Call<MyObject> createRecord(...);

et pas

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

(qui nécessitera toujours un adaptateur)


L'appel vous permettra alors de continuer à utiliser isSuccessfulcar il renvoie réellement une réponse. Vous pouvez donc faire quelque chose comme:

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

Ou accédez à votre type (MyObject) comme:

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

1
public interface SimpleService {

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

}

La communication avec le réseau se fait avec le thread séparé, vous devez donc changer votre Simple avec cela.

public interface SimpleService {

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

}

1

Dans mon cas, j'ai utilisé

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

au lieu de

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

vous devriez utiliser com.squareup.retrofit2:adapter-rxjava2:2.5.0lors de l'utilisationio.reactivex.rxjava2


0

Vous pouvez implémenter un rappel, obtenir la fonction Simple de 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

La façon dont j'ai résolu ce problème a été d'ajouter ceci au fichier gradle de l'application.Si la configuration n'est pas définie, il y aura des conflits, peut-être que cela sera corrigé dans la version stable de la bibliothèque:

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

et créez votre adaptateur de cette façon:

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

J'espère que cela aide!


1
C'est exactement la même configuration que dans ma question. Donc, ne résout rien :)
rmuller
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.