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
rmuller
źródło
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.

Hosam Aly
źródło
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>
}
chatlanin
źródło
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>.

shichaohui
źródło
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()
Himanshu Virmani
źródło
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);
}
Mateusz Korwel
źródło
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);
rahulrv
źródło
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

Hüseyin Bülbül
źródł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)
Johannes Barop
źródło
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

mehdi parsaei
źródło
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();
Nick Cardoso
źródło
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);

}
Syed Waqas
źródło
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

svkaka
źródło
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
    }

}
Sithu
źródło
-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!

Rensodarwin
źródło
1
To jest dokładnie taka sama konfiguracja, jak w moim pytaniu. Więc nic nie rozwiązuje :)
rmuller