Неможливо створити адаптер виклику для прикладу класу. Просто


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)

Чого мені не вистачає? Я знаю, що обгортання типу return Callроботою. Але я хочу, щоб служба повертала бізнес-об'єкти як тип (і працюють у режимі синхронізації).

ОНОВЛЕННЯ

Після додавання додаткових залежностей та, .addCallAdapterFactory(RxJavaCallAdapterFactory.create())як пропонують різні відповіді, я все одно отримую цю помилку:

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


Для тих, хто використовує Coroutine, перевірте відповідь @chatlanin
NJ

Відповіді:


66

Коротка відповідь: повернення Call<Simple> у ваш інтерфейс обслуговування.

Схоже, Retrofit 2.0 намагається знайти спосіб створення об’єкта проксі для вашого сервісного інтерфейсу. Він очікує, що ви напишете це:

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

Однак він все ще хоче грати добре і бути гнучким, коли ви не хочете повертати a Call. На підтримку цього існує концепція a CallAdapter, яка повинна знати, як адаптувати a Call<Simple>в aSimple .

Використання RxJavaCallAdapterFactoryкорисно лише у випадку, якщо ви намагаєтесь повернути rx.Observable<Simple>.

Найпростішим рішенням є повернення, Callяк очікує модернізація. Ви також можете написати, CallAdapter.Factoryякщо вам це дійсно потрібно.


1
Так, це справді правильна відповідь :(. З самого початку я знаю, Call<Simple>як це працює. Однак, як зазначено у моєму запитанні, я віддаю перевагу просто поверненню Simple(як це було у попередній версії). Мій висновок: : неможливо без додаткового коду
rmuller

Посилання, яке ви надали Call<Simple> , порушено. Який пакет Simpleналежить?
shyam

Дякую за примітку @shyam. Я оновив посилання. Simpleце клас, згаданий у питанні OP. Посилання на Call<T>.
Хосам Алі

76

У випадку з 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>.


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-запит. Здається, це не так просто, як вони рекламують. Можливо, їм слід трохи більше задокументувати.
Владо Панджич

Використовуйте модернізацію замість RestAdapter, якщо ви використовуєте модернізацію 2.0.
Хіманшу Вірмані

5
RxJavaCallAdapterFactory вимагає артефакт adapter-rxjava з того самого репо. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl

3
для модернізації2 переконайтесь, що ви використовуєте правильні залежності 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 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це клас. Я відредагував свій код, щоб бути більш актуальним. Дякую.
Матеуш Корвель

@MateuszKorwel Він завжди отримує новий RuntimeException (e)! Я хочу повернути рядок замість простого.
Dr.jacky

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

Це те саме, що і моя установка.
rmuller

1
також обов’язково додайте цей рядок під час створення адаптера: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan

Не вдалося вирішити: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank

1
@ozmank його модернізація2, я теж оновив свою відповідь. Будь ласка, спробуйте
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

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

Response<MyObject> createRecord(...);

стає

Call<MyObject> createRecord(...);

і ні

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

(для чого все одно знадобиться адаптер)


Потім дзвінок дозволить вам використовувати isSuccessfulйого, оскільки він фактично повертає відповідь. Тож ви можете зробити щось на зразок:

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

Я вирішив цю проблему, додавши це до файлу 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
Це точно така ж конфігурація, як у моєму запитанні. Отже, нічого не вирішує :)
rmuller
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.