Reputation: 12849
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
Upvotes: 205
Views: 192661
Reputation: 105
In my case it was working on the debug version but crashing in the release version. I fixed it by updating my proguard-rules.pro
file with:
-keep class retrofit2.** { *; }
Upvotes: 6
Reputation: 2376
I used Coroutines with CoroutineCallAdapterFactory, but accidentally forgot to make the function suspend
. Hope it helps someone!
Upvotes: 57
Reputation: 181
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.
Upvotes: 18
Reputation: 4649
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)
}
}
}
Upvotes: 0
Reputation: 2377
I have found a solution, in my situation is, I am using ApiInterface.java
from JAVA to connect with the MyViewModel.kt
which is from KOTLIN.
This is ApiInterface.java
@GET(Constant.CONNECTION) Call<com.example.kt.model.ConnectionResponse> ConnectionKt();
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() } }
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
Upvotes: 0
Reputation: 6105
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>
}
Upvotes: 547
Reputation: 729
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
Upvotes: 12
Reputation: 7796
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)
Upvotes: 3
Reputation: 4022
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
Upvotes: 1
Reputation: 11
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);
}
Upvotes: 1
Reputation: 21733
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();
Upvotes: 2
Reputation: 1148
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);
}
Upvotes: 19
Reputation: 187
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
Upvotes: 4
Reputation: 5618
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);
Upvotes: 13
Reputation: 4421
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
}
}
Upvotes: 0
Reputation: 42443
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.
Upvotes: 93
Reputation: 579
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>
.
Upvotes: 57
Reputation: 286
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!
Upvotes: -2
Reputation: 2460
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()
Upvotes: 39