Unable to create call adapter for class example.Simple

117,595

Solution 1

Short answer: return Call<Simple> in your service interface.

It looks like Retrofit 2.0 is trying to find a way of creating the proxy object for your service interface. It expects you to write this:

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

However, it still wants to play nice and be flexible when you don't want to return a Call. To support this, it has the concept of a CallAdapter, which is supposed to know how to adapt a Call<Simple> into a Simple.

The use of RxJavaCallAdapterFactory is only useful if you are trying to return rx.Observable<Simple>.

The simplest solution is to return a Call as Retrofit expects. You could also write a CallAdapter.Factory if you really need it.

Solution 2

In case of Kotlin and coroutines this situation happened when I forgot to mark api service function as suspend when I call this function from CoroutineScope(Dispatchers.IO).launch{}:

Usage:

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

Solution 3

add dependencies:

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'

create your adapter this way:

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

addCallAdapterFactory () and addConverterFactory () both need to be called.

Service:

public interface SimpleService {

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

}

Modify Simple to Call<Simple>.

Solution 4

With the new Retrofit(2.+) you need to add addCallAdapterFactory which can be a normal one or a RxJavaCallAdapterFactory(for Observables). I think you can add more than both too. It automatically checks which one to use. See a working example below. You can also check this link for more details.

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

Solution 5

If you want use retrofit2 and you don't want always return retrofit2.Call<T>, you have to create your own CallAdapter.Factory which return simple type as you expected. The simple code can look like this:

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

Then simple register the SynchronousCallAdapterFactory in Retrofit should solved your problem.

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

After that you can return simple type without retrofit2.Call.

public interface SimpleService {
    @GET("/simple/{id}")
    Simple getSimple(@Path("id") String id);
}
Share:
117,595
rmuller
Author by

rmuller

During the last 15 years transformed slowly from a business consultant and senior line manager into a software engineering geek :) Special interests: All facets of managing Product Data in commercial environments (mostly supply side), both technical and organisational related. In depth knowledge of and experience with: Product Information Management (PIM). Heiler (now Informatica), Stibo, Hybris (now SAP), Riversand and many other mid market products (Product) Data Quality, including "smart" data cleansing and data enrichment (Product) Classification systems (like ETIM, eClass) (Product) Search, especially faceted search (primarily Lucene, but also Elastic Search and SOLR) Semantic Web (SPARQL) Data Integration/Migration. ESB's (Specialized in Apache Camel), EDIINT Base technology: Java XML, XSLT, XSL:FO (XEP, Apache FOP), XML Schema, JAXB Web Services, REST, HATEOAS (dislike: BIG Web Services / SOAP) Vaadin (The best WebUI framework I know) Databases. Favorites: PostgreSQL (traditional), H2Database (embedded), NoSQL (Neo4J), MapDB (low level) Ansible Linux (favorite: Debian)

Updated on February 11, 2022

Comments

  • rmuller
    rmuller over 2 years

    I am using retrofit 2.0.0-beta1 with SimpleXml. I want the retrieve a Simple (XML) resource from a REST service. Marshalling/Unmarshalling the Simple object with SimpleXML works fine.

    When using this code (converted form pre 2.0.0 code):

    final Retrofit rest = new Retrofit.Builder()
        .addConverterFactory(SimpleXmlConverterFactory.create())
        .baseUrl(endpoint)
        .build();
    SimpleService service = rest.create(SimpleService.class);
    LOG.info(service.getSimple("572642"));
    

    Service:

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

    I get this exception:

    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)
    

    What am i missing? I know that wrapping the return type by a Call works. But I want the service to return business objects as type (and working in sync mode).

    UPDATE

    After added the extra dependancies and .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) as suggested by different answers, I still get this error:

    Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for class simple.Simple. Tried:
     * retrofit.RxJavaCallAdapterFactory
     * retrofit.DefaultCallAdapter$1
    
  • rmuller
    rmuller almost 9 years
    Great link! Will check later.
  • Vlado Pandžić
    Vlado Pandžić almost 9 years
    In the documentation there is still RestAdapter and there is no example how to make most simple request with Retrofit 2.0. I get error Could not resolve RxJavaCallAdapterFactory. Spent few hours to make most simple android http request. It seems it is no so simple as they advertise. Maybe they should document a bit more.
  • Himanshu Virmani
    Himanshu Virmani almost 9 years
    Use Retrofit instead of RestAdapter if you are using Retrofit 2.0.
  • Himanshu Virmani
    Himanshu Virmani almost 9 years
    @rmuller Please mark the answer accepted if you find it working as that will help other too..
  • Himanshu Virmani
    Himanshu Virmani almost 9 years
    @Vlado Are you using all the needed gradle dependencies? Make sure you add compile "com.squareup.retrofit:adapter-rxjava:2.0.0-beta1" if you want to use rx related adapters. See the link mentioned in answer above.
  • Vlado Pandžić
    Vlado Pandžić almost 9 years
    Oh, I didn't installed that dependency.I installed version 1.9 which is documented a bit.more.
  • rmuller
    rmuller almost 9 years
    Checked it out. Your code does not work for my example code. My conclusion is that I need a Custom CallAdapter for this use case. Also note that RxJavaCallAdapterFactory needs a extra dependency. Correct?
  • Damien Diehl
    Damien Diehl almost 9 years
    RxJavaCallAdapterFactory requires the adapter-rxjava artifact from the same repo. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
  • rmuller
    rmuller almost 9 years
    I know that changing Simple to Call<Simple> does the trick. However, I do not want to introduce type Call to the service interface
  • rmuller
    rmuller almost 9 years
    This is exactly the same configuration as in my question. So, does not resolve anything :)
  • rmuller
    rmuller almost 9 years
    This is the same as my setup.
  • rmuller
    rmuller almost 9 years
    This still does not resolve my issue, see updated question
  • rmuller
    rmuller over 8 years
    Yes, this is indeed the right answer :(. I know from the start that Call<Simple> is the way it works. However, as stated in my question my preference is to just return Simple (as was the case in the earlier version). My conclusion is: not possible without extra code.
  • shyam
    shyam over 8 years
    The link you've provided for Call<Simple> is broken. What package does Simple belong to?
  • Hosam Aly
    Hosam Aly over 8 years
    Thanks for the note @shyam. I've updated the links. Simple is the class mentioned in the OP's question. The link is to Call<T>.
  • markov00
    markov00 about 8 years
    Awesome thanks. Do you know if there is a way to create an async call adapter? so I can call a service like that: service.getSimple("id",new Adapter(){ onFail(){} onSuccess(){} } instead of using the enqueue() and the callback?
  • Shayan_Aryan
    Shayan_Aryan about 8 years
    also be sure to add this line when creating adapter: .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  • George Papas
    George Papas almost 8 years
    for retrofit2, ensure you use the correct dependencies com.squareup.retrofit2:adapter-rxjava:2.1.0 and com.squareup.retrofit2:converter-gson:2.1.0
  • ozmank
    ozmank almost 8 years
    Failed to resolve: com.squareup.retrofit:adapter-rxjava:2.1.0
  • ozmank
    ozmank almost 8 years
    "implements CallAdapter.Factory" This is a class not an interface?
  • ozmank
    ozmank almost 8 years
    Failed to resolve: com.squareup.retrofit:adapter-rxjava:2.1.0
  • rahul.ramanujam
    rahul.ramanujam almost 8 years
    @ozmank its retrofit2 , I have updated my answer too. Please try it out
  • Mateusz Korwel
    Mateusz Korwel over 7 years
    @ozmank In version 2.0.0-beta3 it was an interface, now in version 2.1.0 it is a class. I edited my code, to be more actual. Thanks.
  • Dr.jacky
    Dr.jacky almost 7 years
    @MateuszKorwel It always get throw new RuntimeException(e)! I wanna return String instead of Simple.
  • Mateusz Korwel
    Mateusz Korwel almost 7 years
    @Mr.Hyde It should work. Maybe you rest service dont work? What exception you get when you call call.execute().body()?
  • Jared Burrows
    Jared Burrows almost 7 years
    Thank you posting this. I have created a library around this handling Simple getSimple() and Response<Simple> getSimple() requests: github.com/jaredsburrows/retrofit2-synchronous-adapter.
  • Richard Collette
    Richard Collette almost 6 years
    The code in this answer does not throw an HttpException on a response error. I switched to using the solution from @JaredBurrows.
  • Johannes Barop
    Johannes Barop about 5 years
    You can also use CompletableFuture See stackoverflow.com/questions/32269064/…
  • Johannes Barop
    Johannes Barop about 5 years
    You don't need any other adapter if you're using CompletableFuture See stackoverflow.com/questions/32269064/…
  • Guilherme Ramos
    Guilherme Ramos over 3 years
    You saved my live bro s2
  • Abubakar Rafi
    Abubakar Rafi over 3 years
    whilst using Coroutines us must use suspend with your Function . that helped thanks buddy
  • mmdreza baqalpour
    mmdreza baqalpour over 3 years
    it worked for me tnx. adding suspend is the solution to this error
  • user10997009
    user10997009 over 3 years
    Hello @chatlanin,I am new with MVVM and corotuines,i got the same problem.Can you help me.Here is my code github.com/diptoroy/RetrofitKotlin
  • AbhayBohra
    AbhayBohra almost 3 years
    putting suspend works..but how? can you please explain how did it work
  • Barrufet
    Barrufet almost 3 years
    Saved my time, ty :)
  • Bitwise DEVS
    Bitwise DEVS over 2 years
    I was annoyed that all I see is RxJava related answer here and there, but the OP's actual code sample does not even use RxJava. This is the answer I was looking for!
  • Максим Сыроватка
    Максим Сыроватка about 2 years
    In my case this error was because i forgot in service mark func us suspend. Thanx for answer. :)