183

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
  • 12,062
  • 4
  • 64
  • 92
  • Related: http://stackoverflow.com/questions/32617770/how-to-get-response-as-string-using-retrofit-without-using-gson-or-any-other-lib – Hosam Aly Oct 18 '15 at 20:29
  • 4
    For those who are using Coroutine check @chatlanin answer – N J Feb 04 '20 at 09:40

19 Answers19

463

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>
}
chatlanin
  • 5,257
  • 2
  • 16
  • 16
88

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.

Hosam Aly
  • 41,555
  • 36
  • 141
  • 182
  • 1
    Yes, this is indeed the right answer :(. I know from the start that `Call` 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. – rmuller Oct 19 '15 at 19:16
  • The link you've provided for `Call` is broken. What package does `Simple` belong to? – shyam Nov 17 '15 at 11:27
  • 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`. – Hosam Aly Nov 17 '15 at 11:59
57

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

shichaohui
  • 579
  • 1
  • 3
  • 4
  • 7
    I know that changing Simple to Call does the trick. However, I do not want to introduce type `Call` to the service interface – rmuller Sep 16 '15 at 14:38
  • You can also use `CompletableFuture` See https://stackoverflow.com/questions/32269064/unable-to-create-call-adapter-for-class-example-simple/55652910#55652910 – Johannes Barop Apr 12 '19 at 13:36
38

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()
Himanshu Virmani
  • 2,450
  • 1
  • 24
  • 34
  • 1
    Great link! Will check later. – rmuller Aug 28 '15 at 17:42
  • 1
    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. – Vlado Pandžić Aug 30 '15 at 17:11
  • Use Retrofit instead of RestAdapter if you are using Retrofit 2.0. – Himanshu Virmani Aug 31 '15 at 07:48
  • @rmuller Please mark the answer accepted if you find it working as that will help other too.. – Himanshu Virmani Aug 31 '15 at 07:49
  • @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. – Himanshu Virmani Aug 31 '15 at 07:51
  • Oh, I didn't installed that dependency.I installed version 1.9 which is documented a bit.more. – Vlado Pandžić Aug 31 '15 at 08:44
  • 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? – rmuller Sep 01 '15 at 15:10
  • 5
    RxJavaCallAdapterFactory requires the adapter-rxjava artifact from the same repo. ```compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'``` – Damien Diehl Sep 05 '15 at 00:33
  • This still does not resolve my issue, see updated question – rmuller Sep 16 '15 at 14:40
  • 3
    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` – George Papas Jun 30 '16 at 01:34
  • Failed to resolve: com.squareup.retrofit:adapter-rxjava:2.1.0 – ozmank Sep 26 '16 at 09:53
  • You don't need any other adapter if you're using `CompletableFuture` See https://stackoverflow.com/questions/32269064/unable-to-create-call-adapter-for-class-example-simple/55652910#55652910 – Johannes Barop Apr 12 '19 at 13:37
35

I used Coroutines with CoroutineCallAdapterFactory, but accidentally forgot to make the function suspend. Hope it helps someone!

Merkost
  • 1,251
  • 9
  • 16
19

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);
}
Mateusz Korwel
  • 1,118
  • 1
  • 8
  • 14
  • 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? – markov00 Apr 11 '16 at 10:51
  • "implements CallAdapter.Factory" This is a class not an interface? – ozmank Sep 26 '16 at 09:48
  • @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. – Mateusz Korwel Sep 28 '16 at 23:00
  • @MateuszKorwel It always get throw new RuntimeException(e)! I wanna return String instead of Simple. – Dr.jacky Aug 01 '17 at 07:11
  • @Mr.Hyde It should work. Maybe you rest service dont work? What exception you get when you call `call.execute().body()`? – Mateusz Korwel Aug 01 '17 at 16:14
  • 3
    Thank you posting this. I have created a library around this handling `Simple getSimple()` and `Response getSimple()` requests: https://github.com/jaredsburrows/retrofit2-synchronous-adapter. – Jared Burrows Aug 29 '17 at 23:42
  • The code in this answer does not throw an HttpException on a response error. I switched to using the solution from @JaredBurrows. – Richard Collette Aug 31 '18 at 20:52
13

Add the following dependencies for retrofit 2

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

for GSON

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

for observables

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

In your case for XML , you would have to include the following dependencies

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

Update the service call as below

final Retrofit rest = new Retrofit.Builder()
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .baseUrl(endpoint)
    .build();
SimpleService service = rest.create(SimpleService.class);
rahul.ramanujam
  • 5,608
  • 7
  • 34
  • 56
12

IllegalArgumentException: Unable to create call adapter for class java.lang.Object

Short answer: I have solved it by the following changes

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"

Good luck

ItSNeverLate
  • 533
  • 7
  • 8
10

For this case :

val apiService = RetrofitFactory.makeRetrofitService()

CoroutineScope(Dispatchers.IO).launch {

    val response = apiService.myGetRequest()

    // process response...

}

interface ApiService {

   @GET("/my-get-request")
   suspend fun myGetRequest(): Response<String>
}

All methods used within suspend functions must be marked as suspend because of it's manageability by the Corountine. Check more info here.

So if you dont mark your API service function as suspend when you call it inside a CoroutineScope or any suspend marked method, android app crashes and throws the Exception in thread "main" java.lang.IllegalArgumentException: Unable to create call adapter for class example.class error.

lucas rivaldo
  • 101
  • 1
  • 3
4

in my case using this

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

with this

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

solved the problem when nothing else worked

3

If you are not using RxJava it properly makes no sense to add RxJava just for retrofit. 2.5.0 has support for CompletableFuture built in which you can use without adding any other library or adapter.

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

Usage:

Retrofit.Builder()
   .addConverterFactory(SimpleXmlConverterFactory.create())
   .baseUrl("https://api.example.com")
   .build()
   .create(Api::class.java)
Johannes Barop
  • 7,323
  • 2
  • 25
  • 33
2

Just to make the Call examples clearer for people who are migrating, not using Rx, or who want synchronous calls - Call essentially replaces (wraps) Response, meaning:

Response<MyObject> createRecord(...);

becomes

Call<MyObject> createRecord(...);

and not

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

(which will still require an adapter)


The Call will then allow you to still use isSuccessful as it actually returns a Response. So you can do something like:

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

Or access your Type (MyObject) like:

MyObject myObj = myApi.createRecord(...).execute().body();
Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
1
public interface SimpleService {

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

}

Communication with the network is done with the separate thread so you should change your Simple with that.

public interface SimpleService {

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

}
Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
Syed Waqas
  • 11
  • 1
1

In my case I used

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

instead of

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

you should be using com.squareup.retrofit2:adapter-rxjava2:2.5.0 when using io.reactivex.rxjava2

svkaka
  • 3,942
  • 2
  • 31
  • 55
0

You can implement a Callback, get the Simple from onResponse function.

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
  • 4,241
  • 1
  • 20
  • 28
0

I have found a solution, in my situation is, I am using ApiInterface.java from JAVA to connect with the MyViewModel.ktwhich is from KOTLIN.

  1. This is ApiInterface.java

    @GET(Constant.CONNECTION) Call<com.example.kt.model.ConnectionResponse> ConnectionKt();

  2. You need to create a new Kotlin class, named as ApiTemporary.kt

    class ApiTemporary( private val apiInterface: ApiInterface = RetrofitClient.getRetrofitInstance().create(ApiInterface::class.java) ) {

    suspend fun getConnectionKt(): Response { return apiInterface.ConnectionKt().awaitResponse() } }

  3. So lastly, at MyViewModel.kt, you can do like this

val data = withContext(ioDispatcher) { val result = apiTemporary.getConnectionKt() if (result.isSuccessful) { Resource.Success(result.body()) } else { Resource.Error("Server error. Please try again") } } _connection.value = data

So, the ApiTemporary will help to convert them from Java to Kotlin which is usage of Coroutines

Ticherhaz FreePalestine
  • 2,738
  • 4
  • 20
  • 46
0

I solved by update retrofit version to 2.9.0 in gradle

0

If your API interface is returning Response object then probably you forgot to add suspend function in your API interface.

API Interface

interface WeatherAPI {
    @GET("/data/2.5/weather")
    suspend fun getWeatherInfo(
        @Query("q") query: String
    ): Response<WeatherResponse>
}

Repository

class WeatherRepositoryImpl @Inject constructor(
private val weatherAPI: WeatherAPI
) : WeatherRepository {
    override suspend fun getWeatherInfo(query: String): Resource<WeatherResponse> {
        try {
            val response = weatherAPI.getWeatherInfo(query)
            if (response.isSuccessful) {
                response.body()?.let {
                    return Resource.success(it)
                } ?: return Resource.error("Unknown error occurred", null)
            } else {
                return Resource.error("Unknown error occured", null)
            }
        } catch (e: Exception) {
            return Resource.error("Please check your internet connection", null)
        }
    }

}
Gopal Singh Sirvi
  • 4,539
  • 5
  • 33
  • 55
-2

The way I fixed this issue was adding this to the application gradle file, if the configuration is not set there will be conflicts, maybe this will be fixed in the stable release of the library:

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

and create your adapter this way:

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

Hope it helps!

Rensodarwin
  • 286
  • 4
  • 12