Nie można utworzyć adaptera wywołań dla przykładu klasy Simple


87

Używam retrofitu 2.0.0-beta1 z SimpleXml. Chcę pobrać zasób Simple (XML) z usługi REST. Marshalling / Unmarshalling obiektu Simple za pomocą SimpleXML działa dobrze.

W przypadku korzystania z tego kodu (przekonwertowanego z kodu sprzed 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"));

Usługa:

public interface SimpleService {

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

}

Mam ten wyjątek:

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)

Czego mi brakuje? Wiem, że zawijanie typu powrotu przez a Calldziała. Ale chcę, aby usługa zwracała obiekty biznesowe jako typ (i działała w trybie synchronizacji).

AKTUALIZACJA

Po dodaniu dodatkowych zależności i .addCallAdapterFactory(RxJavaCallAdapterFactory.create())zgodnie z sugestiami różnych odpowiedzi nadal otrzymuję ten błąd:

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


Dla tych, którzy używają Coroutine, sprawdź @chatlanin answer
NJ

Odpowiedzi:


66

Krótka odpowiedź: wróć Call<Simple>do interfejsu usługi.

Wygląda na to, że Retrofit 2.0 próbuje znaleźć sposób na utworzenie obiektu proxy dla interfejsu usługi. Oczekuje, że napiszesz to:

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

Jednak nadal chce grać dobrze i być elastycznym, gdy nie chcesz zwracać pliku Call. Aby to wesprzeć, ma koncepcję a CallAdapter, która ma wiedzieć, jak dostosować a Call<Simple>do Simple.

Użycie RxJavaCallAdapterFactoryjest przydatne tylko wtedy, gdy próbujesz wrócić rx.Observable<Simple>.

Najprostszym rozwiązaniem jest zwrócenie Callzgodnie z oczekiwaniami Retrofit. Możesz też napisać, CallAdapter.Factoryjeśli naprawdę tego potrzebujesz.


1
Tak, to jest rzeczywiście prawidłowa odpowiedź :(. Wiem od początku, że Call<Simple>tak to działa. Jednak, jak napisałem w moim pytaniu, wolę po prostu wrócić Simple(tak jak to miało miejsce we wcześniejszej wersji). : nie jest możliwe bez dodatkowego kodu
rmuller

Podany link Call<Simple> jest uszkodzony. Do jakiego pakietu Simplenależy?
shyam

Dzięki za notatkę @shyam. Zaktualizowałem linki. Simpleto klasa, o której mowa w pytaniu PO. Link to Call<T>.
Hosam Aly,

75

W przypadku Kotlina i coroutinesów taka sytuacja miała miejsce, gdy zapomniałem oznaczyć funkcję usługi API tak, jak wywołujęsuspend tę funkcję z CoroutineScope(Dispatchers.IO).launch{}:

Stosowanie:

    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

dodaj zależności:

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'

stwórz swój adapter w ten sposób:

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

addCallAdapterFactory ()i addConverterFactory ()oboje muszą zostać wezwani.

Usługa:

public interface SimpleService {

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

}

Zmień Simplena Call<Simple>.


7
Wiem, że zmiana Simple to Call <Simple> załatwia sprawę. Jednak nie chcę wprowadzać typu Calldo interfejsu usługi
rmuller

Możesz również użyć CompletableFutureZobacz stackoverflow.com/questions/32269064/…
Johannes Barop

38

Z nowym Retrofitem (2. +) musisz dodać addCallAdapterFactory, który może być normalny lub RxJavaCallAdapterFactory (dla Observables). Myślę, że możesz dodać więcej niż jedno i drugie. Automatycznie sprawdza, którego użyć. Zobacz działający przykład poniżej. Możesz również sprawdzić ten link, aby uzyskać więcej informacji.

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

1
Świetny link! Sprawdzę później.
rmuller

1
W dokumentacji wciąż znajduje się RestAdapter i nie ma przykładu jak wykonać najprostsze żądanie z Retrofit 2.0. Pojawia się błąd Nie można rozwiązać RxJavaCallAdapterFactory. Poświęcono kilka godzin, aby wykonać najprostsze żądanie http dla Androida. Wydaje się, że nie jest to takie proste, jak się reklamują. Może powinni udokumentować trochę więcej.
Vlado Pandžić

Użyj Retrofit zamiast RestAdapter, jeśli używasz Retrofit 2.0.
Himanshu Virmani

5
RxJavaCallAdapterFactory wymaga artefaktu adapter-rxjava z tego samego repozytorium. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl,

3
w przypadku modernizacji2 upewnij się, że używasz poprawnych zależności com.squareup.retrofit2:adapter-rxjava:2.1.0i com.squareup.retrofit2:converter-gson:2.1.0
George Papas

16

Jeśli chcesz używać retrofit2i nie chcesz zawsze zwracać retrofit2.Call<T>, musisz utworzyć własny, CallAdapter.Factoryktóry zwraca prosty typ zgodnie z oczekiwaniami. Prosty kod może wyglądać następująco:

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

Wtedy prosta rejestracja SynchronousCallAdapterFactoryw Retrofit powinna rozwiązać twój problem.

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

Następnie możesz zwrócić prosty typ bez retrofit2.Call.

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

Wielkie dzieki. Czy wiesz, czy istnieje sposób na utworzenie adaptera wywołań asynchronicznych? więc mogę wywołać taką usługę: service.getSimple ("id", nowy Adapter () {onFail () {} onSuccess () {}} zamiast używać enqueue () i wywołania zwrotnego?
markov00

„implementuje CallAdapter.Factory” To jest klasa, a nie interfejs?
ozmank

@ozmank W wersji 2.0.0-beta3był to interfejs, teraz w wersji2.1.0 jest to klasa. Zredagowałem swój kod, żeby był bardziej aktualny. Dzięki.
Mateusz Korwel

@MateuszKorwel Zawsze rzuca się nowy RuntimeException (e)! Chcę zwrócić String zamiast Simple.
Dr Jacky

3
Dziękuję za opublikowanie tego. Stworzyłem bibliotekę wokół tej obsługi Simple getSimple()iResponse<Simple> getSimple() żądań: github.com/jaredsburrows/retrofit2-synchronous-adapter .
Jared Burrows

13

Dodaj następujące zależności dla modernizacji 2

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

dla GSON

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

dla obserwacji

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

W twoim przypadku dla XML, musisz uwzględnić następujące zależności

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

Zaktualizuj zgłoszenie serwisowe, jak poniżej

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

To jest to samo, co moja konfiguracja.
rmuller,

1
pamiętaj również o dodaniu tego wiersza podczas tworzenia adaptera: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan

Nie udało się rozwiązać: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank

1
@ozmank jego retrofit2, zaktualizowałem też moją odpowiedź. Wypróbuj to
rahulrv

4

w moim przypadku używając tego

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

z tym

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

rozwiązał problem, gdy nic innego nie działało


2

Jeśli nie używasz RxJava, nie ma sensu dodawać RxJava tylko do modernizacji. 2.5.0ma CompletableFuturewbudowaną obsługę, z której można korzystać bez dodawania innej biblioteki lub adaptera.

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

Stosowanie:

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

2

IllegalArgumentException: nie można utworzyć adaptera wywołań dla klasy java.lang.Object

Krótka odpowiedź: rozwiązałem to poprzez następujące zmiany

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"

Powodzenia


1

Aby wyjaśnić przykłady połączeń dla osób, które migrują, nie używają Rx lub chcą połączeń synchronicznych - Call zasadniczo zastępuje (zawija) odpowiedź, co oznacza:

Response<MyObject> createRecord(...);

staje się

Call<MyObject> createRecord(...);

i nie

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

(który nadal będzie wymagał adaptera)


Wezwanie pozwoli ci wtedy nadal używać, isSuccessfulponieważ w rzeczywistości zwraca odpowiedź. Możesz więc zrobić coś takiego:

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

Lub uzyskaj dostęp do swojego Type (MyObject), na przykład:

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

1
public interface SimpleService {

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

}

Komunikacja z siecią odbywa się za pomocą osobnego wątku, więc powinieneś zmienić swój Simple.

public interface SimpleService {

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

}

1

W moim przypadku użyłem

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

zamiast

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

powinieneś używać com.squareup.retrofit2:adapter-rxjava2:2.5.0podczas używaniaio.reactivex.rxjava2


0

Możesz zaimplementować Callback, pobrać funkcję 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

Sposób, w jaki naprawiłem ten problem, polegał na dodaniu tego do pliku oceny aplikacji, jeśli konfiguracja nie jest ustawiona, będą konflikty, być może zostanie to naprawione w stabilnej wersji biblioteki:

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

i stwórz swój adapter w ten sposób:

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

Mam nadzieję, że to pomoże!


1
To jest dokładnie taka sama konfiguracja, jak w moim pytaniu. Więc nic nie rozwiązuje :)
rmuller
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.