Não é possível criar um adaptador de chamada para a classe exemplo. Simples


87

Estou usando o retrofit 2.0.0-beta1 com SimpleXml. Quero recuperar um recurso Simples (XML) de um serviço REST. Marshalling / Unmarshalling o objeto Simple com SimpleXML funciona bem.

Ao usar este código (formato convertido antes do código 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"));

Serviço:

public interface SimpleService {

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

}

Eu recebo esta exceção:

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)

O que estou perdendo? Eu sei que envolver o tipo de retorno por uma Callfunciona. Mas eu quero que o serviço retorne objetos de negócios como tipo (e trabalhando em modo de sincronização).

ATUALIZAR

Depois de adicionar as dependências extras e .addCallAdapterFactory(RxJavaCallAdapterFactory.create())conforme sugerido por diferentes respostas, ainda recebo este erro:

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


Para quem está usando o Coroutine, verifique a resposta em @chatlanin
NJ

Respostas:


66

Resposta curta: retorne Call<Simple>em sua interface de serviço.

Parece que o Retrofit 2.0 está tentando encontrar uma maneira de criar o objeto proxy para sua interface de serviço. Ele espera que você escreva isto:

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

No entanto, ele ainda quer jogar bem e ser flexível quando você não quiser devolver um Call. Para apoiar isso, ele tem o conceito de a CallAdapter, que deve saber como adaptar a Call<Simple>em a Simple.

O uso de RxJavaCallAdapterFactorysó é útil se você estiver tentando retornar rx.Observable<Simple>.

A solução mais simples é retornar um Callcomo o Retrofit espera. Você também pode escrever um CallAdapter.Factoryse realmente precisar.


1
Sim, esta é realmente a resposta certa :(. Sei desde o início que Call<Simple>é assim que funciona. No entanto, conforme declarado na minha pergunta, minha preferência é apenas retornar Simple(como era o caso na versão anterior). Minha conclusão é : não é possível sem código extra.
rmuller

O link que você forneceu Call<Simple> está quebrado. A qual pacote Simplepertence?
shyam

Obrigado pela nota @shyam. Eu atualizei os links. Simpleé a classe mencionada na pergunta do OP. O link é para Call<T>.
Hosam Aly de

74

No caso de Kotlin e corrotinas, essa situação aconteceu quando eu esqueci de marcar a função de serviço da API como suspendquando chamo essa função de CoroutineScope(Dispatchers.IO).launch{}:

Uso:

    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

adicionar dependências:

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'

crie seu adaptador desta forma:

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

addCallAdapterFactory ()e addConverterFactory ()ambos precisam ser chamados.

Serviço:

public interface SimpleService {

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

}

Modifique Simplepara Call<Simple>.


7
Eu sei que mudar Simples para Chamar <Simple> resolve o problema. No entanto, não quero apresentar o tipo Callà interface de serviço
rmuller

Você também pode usar CompletableFutureVer stackoverflow.com/questions/32269064/…
Johannes Barop

38

Com o novo Retrofit (2. +), você precisa adicionar addCallAdapterFactory que pode ser normal ou RxJavaCallAdapterFactory (para Observables). Acho que você também pode adicionar mais do que ambos. Ele verifica automaticamente qual usar. Veja um exemplo de trabalho abaixo. Você também pode verificar este link para mais detalhes.

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

1
Ótimo link! Vou verificar mais tarde.
rmuller

1
Na documentação ainda há RestAdapter e não há exemplo de como fazer a solicitação mais simples com Retrofit 2.0. Recebo o erro. Não foi possível resolver RxJavaCallAdapterFactory. Passou algumas horas para fazer a solicitação de http do Android mais simples. Parece que não é tão simples como eles anunciam. Talvez eles devessem documentar um pouco mais.
Vlado Pandžić

Use Retrofit em vez de RestAdapter se estiver usando Retrofit 2.0.
Himanshu Virmani

5
RxJavaCallAdapterFactory requer o artefato adapter-rxjava do mesmo repo. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl,

3
para retrofit2, certifique-se de usar as dependências corretas com.squareup.retrofit2:adapter-rxjava:2.1.0e com.squareup.retrofit2:converter-gson:2.1.0
George Papas

16

Se quiser usar retrofit2e não quiser retornar sempre retrofit2.Call<T>, você deve criar o seu próprio CallAdapter.Factorytipo simples de retorno conforme o esperado. O código simples pode ter a seguinte aparência:

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

Então, basta registrar-se SynchronousCallAdapterFactoryno Retrofit para resolver seu problema.

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

Depois disso, você pode retornar um tipo simples sem retrofit2.Call.

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

Muito obrigado. Você sabe se existe uma maneira de criar um adaptador de chamada assíncrona? então posso chamar um serviço como este: service.getSimple ("id", new Adapter () {onFail () {} onSuccess () {}} em vez de usar o enqueue () e o retorno de chamada?
markov00

"implementa CallAdapter.Factory" Esta é uma classe, não uma interface?
Ozmank

@ozmank Na versão 2.0.0-beta3era uma interface, agora na versão 2.1.0é uma classe. Eu editei meu código, para ser mais atual. Obrigado.
Mateusz Korwel

@MateuszKorwel Sempre consigo lançar um novo RuntimeException (e)! Eu quero retornar String em vez de Simple.
Dr.jacky

3
Obrigado por postar isso. Eu criei uma biblioteca em torno deste tratamento Simple getSimple()e Response<Simple> getSimple()solicitações: github.com/jaredsburrows/retrofit2-synchronous-adapter .
Jared Burrows

13

Adicione as seguintes dependências para retrofit 2

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

para GSON

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

para observáveis

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

No seu caso para XML, você teria que incluir as seguintes dependências

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

Atualize a chamada de serviço conforme abaixo

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

Este é o mesmo que minha configuração.
rmuller

1
também certifique-se de adicionar esta linha ao criar o adaptador: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan

Falha ao resolver: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank

1
@ozmank seu retrofit2, atualizei minha resposta também. Experimente
rahulrv

4

no meu caso usar isso

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

com isso

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

resolveu o problema quando nada mais funcionava


2

Se você não estiver usando o RxJava corretamente, não faz sentido adicionar o RxJava apenas para atualização. 2.5.0tem suporte para CompletableFuturebuilt-in que você pode usar sem adicionar qualquer outra biblioteca ou adaptador.

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

Uso:

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

2

IllegalArgumentException: Não é possível criar um adaptador de chamada para a classe java.lang.Object

Resposta curta: Resolvi com as seguintes mudanças

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"

Boa sorte


1

Apenas para tornar os exemplos de chamada mais claros para pessoas que estão migrando, não usando Rx ou que desejam chamadas síncronas - a chamada essencialmente substitui (envolve) a resposta, o que significa:

Response<MyObject> createRecord(...);

torna-se

Call<MyObject> createRecord(...);

e não

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

(que ainda exigirá um adaptador)


A Chamada permitirá que você ainda use, isSuccessfulpois ela realmente retorna uma Resposta. Então você pode fazer algo como:

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

Ou acesse seu tipo (MyObject) como:

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

1
public interface SimpleService {

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

}

A comunicação com a rede é feita com o segmento separado, então você deve alterar o seu Simples com ele.

public interface SimpleService {

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

}

1

No meu caso eu usei

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

ao invés de

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

você deveria estar usando com.squareup.retrofit2:adapter-rxjava2:2.5.0ao usario.reactivex.rxjava2


0

Você pode implementar um retorno de chamada, obter a função Simples 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

A maneira como eu corrigi esse problema foi adicionando isso ao arquivo gradle do aplicativo, se a configuração não for definida haverá conflitos, talvez isso seja corrigido na versão estável da biblioteca:

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

e crie seu adaptador desta forma:

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

Espero que ajude!


1
Esta é exatamente a mesma configuração da minha pergunta. Então, não resolve nada :)
rmuller
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.