Невозможно создать адаптер вызова для примера класса.

87

Я использую модификацию 2.0.0-beta1 с SimpleXml. Я хочу получить простой (XML) ресурс из службы REST. Маршалинг / демаршалинг простого объекта с SimpleXML работает нормально.

При использовании этого кода (преобразованная форма кода до 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"));

Обслуживание:

public interface SimpleService {

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

}

Я получаю это исключение:

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)

Что мне не хватает? Я знаю, что упаковка возвращаемого типа Callработает. Но я хочу, чтобы служба возвращала бизнес-объекты как тип (и работала в режиме синхронизации).

ОБНОВИТЬ

После добавления дополнительных зависимостей и, .addCallAdapterFactory(RxJavaCallAdapterFactory.create())как было предложено в разных ответах, я все еще получаю эту ошибку:

Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for class simple.Simple. Tried:
 * retrofit.RxJavaCallAdapterFactory
 * retrofit.DefaultCallAdapter$1
Рмюллер
источник
Связанный: stackoverflow.com/questions/32617770/…
Хосам Али
Для тех, кто использует Coroutine, проверьте ответ @chatlanin
Нью-Джерси,

Ответы:

66

Короткий ответ: вернитесь Call<Simple>в свой сервисный интерфейс.

Похоже, Retrofit 2.0 пытается найти способ создания прокси-объекта для интерфейса вашей службы. Ожидается, что вы напишете это:

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

Однако он по-прежнему хочет играть хорошо и быть гибким, когда вы не хотите возвращать Call. Чтобы поддержать это, у него есть концепция a CallAdapter, которая должна знать, как Call<Simple>преобразовать a в Simple.

Использование RxJavaCallAdapterFactoryполезно только в том случае, если вы пытаетесь вернуться rx.Observable<Simple>.

Самое простое решение - вернуть в Callсоответствии с требованиями Retrofit. Вы также можете написать, CallAdapter.Factoryесли вам это действительно нужно.

Хосам Али
источник
1
Да, это действительно правильный ответ :(. Я с самого начала знаю, Call<Simple>как это работает. Однако, как указано в моем вопросе, я предпочитаю просто вернуться Simple(как было в более ранней версии). Мой вывод такой: : невозможно без дополнительного кода.
rmuller
Ссылка, которую вы указали, не Call<Simple> работает. К какому пакету Simpleотносится?
shyam
Спасибо за заметку @shyam. Я обновил ссылки. Simple- это класс, упомянутый в вопросе OP. Ссылка на Call<T>.
Хосам Али
75

В случае Kotlin и сопрограмм эта ситуация произошла, когда я забыл отметить служебную функцию api, suspendкогда я вызываю эту функцию из CoroutineScope(Dispatchers.IO).launch{}:

Применение:

    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

добавить зависимости:

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'

создайте свой адаптер таким образом:

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

addCallAdapterFactory ()и addConverterFactory ()оба должны быть вызваны.

Обслуживание:

public interface SimpleService {

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

}

Измените Simpleна Call<Simple>.

Shichaohui
источник
7
Я знаю, что изменение Simple на Call <Simple> помогает. Однако я не хочу вводить тип Callв интерфейс службы
rmuller
Вы можете также использовать CompletableFutureСм stackoverflow.com/questions/32269064/...
Johannes Barop
38

В новом Retrofit (2. +) вам нужно добавить addCallAdapterFactory, который может быть обычным или RxJavaCallAdapterFactory (для Observables). Я думаю, вы тоже можете добавить больше, чем оба. Он автоматически проверяет, какой из них использовать. См. Рабочий пример ниже. Вы также можете проверить эту ссылку для получения более подробной информации.

 Retrofit retrofit = new Retrofit.Builder().baseUrl(ApiConfig.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build()
Химаншу Вирмани
источник
1
Отличная ссылка! Проверю позже.
rmuller
1
В документации еще есть RestAdapter и нет примера, как сделать самый простой запрос с помощью Retrofit 2.0. Я получаю сообщение об ошибке Не удалось разрешить RxJavaCallAdapterFactory. Потратил несколько часов, чтобы сделать самый простой HTTP-запрос для Android. Вроде не все так просто, как рекламируют. Может, им стоит задокументировать еще немного.
Владо Панджич
Используйте Retrofit вместо RestAdapter, если вы используете Retrofit 2.0.
Химаншу Вирмани
5
RxJavaCallAdapterFactory требует артефакта adapter-rxjava из того же репо. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Дэмиен Диль
3
для retrofit2 убедитесь, что вы используете правильные зависимости com.squareup.retrofit2:adapter-rxjava:2.1.0и com.squareup.retrofit2:converter-gson:2.1.0
Джордж Папас
16

Если вы хотите использовать retrofit2и не хотите всегда возвращать retrofit2.Call<T>, вам нужно создать свой собственный, CallAdapter.Factoryкоторый возвращает простой тип, как вы ожидали. Простой код может выглядеть так:

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

Тогда простая регистрация SynchronousCallAdapterFactoryв Retrofit должна решить вашу проблему.

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

После этого вы можете вернуть простой тип без retrofit2.Call.

public interface SimpleService {
    @GET("/simple/{id}")
    Simple getSimple(@Path("id") String id);
}
Матеуш Корвель
источник
Огромное спасибо. Вы знаете, есть ли способ создать адаптер асинхронного вызова? так что я могу вызвать такую ​​службу: service.getSimple ("id", new Adapter () {onFail () {} onSuccess () {}} вместо использования enqueue () и обратного вызова?
markov00
"реализует CallAdapter.Factory" Это класс, а не интерфейс?
ozmank
@ozmank В версии 2.0.0-beta3это был интерфейс, теперь в версии 2.1.0это класс. Я отредактировал свой код, чтобы он был более актуальным. Спасибо.
Mateusz Korwel
@MateuszKorwel Всегда генерирует новое исключение RuntimeException (e)! Я хочу вернуть String вместо Simple.
Доктор Джеки 01
3
Спасибо, что разместили это. Я создал библиотеку для этой обработки Simple getSimple()и Response<Simple> getSimple()запросов: github.com/jaredsburrows/retrofit2-synchronous-adapter .
Джаред Берроуз
13

Добавьте следующие зависимости для модернизации 2

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

для GSON

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

для наблюдаемых

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

В вашем случае для XML вам нужно будет включить следующие зависимости

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

Обновите сервисный вызов, как показано ниже

final Retrofit rest = new Retrofit.Builder()
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .baseUrl(endpoint)
    .build();
SimpleService service = rest.create(SimpleService.class);
rahulrv
источник
Это то же самое, что и моя установка.
rmuller
1
также не забудьте добавить эту строку при создании адаптера: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan
Не удалось разрешить: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank
1
@ozmank its retrofit2, я тоже обновил свой ответ. Пожалуйста, попробуйте
rahulrv
4

в моем случае используя это

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

с этим

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

решил проблему, когда ничего не работало

Хусейн Бюльбюль
источник
2

Если вы не используете RxJava, нет смысла добавлять RxJava только для модернизации. 2.5.0имеет CompletableFutureвстроенную поддержку, которую можно использовать без добавления какой-либо другой библиотеки или адаптера.

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

Применение:

Retrofit.Builder()
   .addConverterFactory(SimpleXmlConverterFactory.create())
   .baseUrl("https://api.example.com")
   .build()
   .create(Api::class.java)
Йоханнес Бароп
источник
2

IllegalArgumentException: невозможно создать адаптер вызова для класса java.lang.Object

Краткий ответ: я решил это следующими изменениями

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"

Удачи

мехди парсаи
источник
1

Просто чтобы сделать примеры Call более понятными для людей, которые мигрируют, не используют Rx или хотят синхронные вызовы - Call по существу заменяет (обертывает) Response, что означает:

Response<MyObject> createRecord(...);

становится

Call<MyObject> createRecord(...);

и нет

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

(для чего по-прежнему потребуется адаптер)


Затем Call позволит вам использовать, isSuccessfulпоскольку фактически возвращает Response. Итак, вы можете сделать что-то вроде:

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

Или получите доступ к своему типу (MyObject), например:

MyObject myObj = myApi.createRecord(...).execute().body();
Ник Кардосо
источник
1
public interface SimpleService {

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

}

Связь с сетью осуществляется с помощью отдельного потока, поэтому вы должны изменить свой Simple.

public interface SimpleService {

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

}
Сайед Вакас
источник
1

В моем случае я использовал

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

вместо того

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

вы должны использовать com.squareup.retrofit2:adapter-rxjava2:2.5.0при использованииio.reactivex.rxjava2

Свкака
источник
0

Вы можете реализовать обратный вызов, получить Simple из функции 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

Я исправил эту проблему, добавив это в файл Gradle приложения, если конфигурация не установлена, возникнут конфликты, возможно, это будет исправлено в стабильной версии библиотеки:

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

и создайте свой адаптер следующим образом:

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

Надеюсь, это поможет!

Ренсодарвин
источник
1
Это точно такая же конфигурация, что и в моем вопросе. Так что ничего не
решает