Tak dapat membuat adaptor panggilan untuk contoh kelas. Sederhana

91

Saya menggunakan retrofit 2.0.0-beta1 dengan SimpleXml. Saya ingin mengambil sumber daya Simple (XML) dari layanan REST. Marshalling / Unmarshalling objek Simple dengan SimpleXML berfungsi dengan baik.

Saat menggunakan kode ini (bentuk yang diubah sebelum kode 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"));

Layanan:

public interface SimpleService {

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

}

Saya mendapatkan pengecualian ini:

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)

Apa yang saya lewatkan? Saya tahu bahwa membungkus tipe pengembalian dengan sebuah Callkarya. Tapi saya ingin layanan mengembalikan objek bisnis sebagai tipe (dan bekerja dalam mode sinkronisasi).

MEMPERBARUI

Setelah menambahkan ketergantungan tambahan dan .addCallAdapterFactory(RxJavaCallAdapterFactory.create())seperti yang disarankan oleh jawaban yang berbeda, saya masih mendapatkan kesalahan ini:

Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for class simple.Simple. Tried:
 * retrofit.RxJavaCallAdapterFactory
 * retrofit.DefaultCallAdapter$1
rmuller
sumber
1
Bagi mereka yang menggunakan Coroutine, periksa jawaban @chatlanin
NJ

Jawaban:

68

Jawaban singkat: kembali Call<Simple>ke antarmuka layanan Anda.

Sepertinya Retrofit 2.0 mencoba menemukan cara untuk membuat objek proxy untuk antarmuka layanan Anda. Ini mengharapkan Anda untuk menulis ini:

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

Namun, tetap ingin bermain bagus dan fleksibel ketika Anda tidak ingin mengembalikan Call. Untuk mendukung hal tersebut, maka memiliki konsep a CallAdapter, yaitu mengetahui bagaimana mengadaptasi a Call<Simple>menjadi a Simple.

Penggunaan RxJavaCallAdapterFactoryhanya berguna jika Anda mencoba mengembalikan rx.Observable<Simple>.

Solusi paling sederhana adalah mengembalikan Callseperti yang diharapkan Retrofit. Anda juga dapat menulis CallAdapter.Factoryjika Anda benar-benar membutuhkannya.

Hosam Aly
sumber
1
Ya, ini memang jawaban yang benar :(. Saya tahu dari awal Call<Simple>cara kerjanya. Namun, seperti yang dinyatakan dalam pertanyaan saya, preferensi saya adalah kembali Simple(seperti yang terjadi di versi sebelumnya). Kesimpulan saya adalah : tidak mungkin tanpa kode tambahan.
rmuller
Tautan yang Anda berikan Call<Simple> rusak. Paket apa yang Simpletermasuk?
malu
Terima kasih atas catatannya @shyam. Saya telah memperbarui tautan. Simpleadalah kelas yang disebutkan dalam pertanyaan OP. Tautannya adalah ke Call<T>.
Hosam Aly
94

Dalam kasus Kotlin dan coroutine , situasi ini terjadi ketika saya lupa menandai fungsi layanan api seperti suspendketika saya memanggil fungsi ini dari CoroutineScope(Dispatchers.IO).launch{}:

Pemakaian:

    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
sumber
Anda menyelamatkan hidup saya bro s2
Guilherme Ramos
saat menggunakan Coroutines, kami harus menggunakan penangguhan dengan Fungsi Anda. yang membantu terima kasih sobat
Abubakar Rafi
itu berhasil untuk saya tnx. menambahkan penangguhan adalah solusi untuk kesalahan ini
mmdreza baqalpour
53

tambahkan dependensi:

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'

buat adaptor Anda dengan cara ini:

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

addCallAdapterFactory ()dan addConverterFactory ()keduanya perlu dipanggil.

Layanan:

public interface SimpleService {

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

}

Ubah Simplemenjadi Call<Simple>.

shichaohui
sumber
7
Saya tahu bahwa mengubah Simple to Call <Simple> berhasil. Namun, saya tidak ingin memperkenalkan tipe Callke antarmuka layanan
rmuller
Anda juga dapat menggunakan CompletableFutureLihat stackoverflow.com/questions/32269064/…
Johannes Barop
38

Dengan Retrofit baru (2. +) Anda perlu menambahkan addCallAdapterFactory yang bisa jadi yang normal atau RxJavaCallAdapterFactory (untuk Observables). Saya pikir Anda juga dapat menambahkan lebih dari keduanya. Secara otomatis memeriksa mana yang akan digunakan. Lihat contoh kerja di bawah ini. Anda juga dapat memeriksa tautan ini untuk lebih jelasnya.

 Retrofit retrofit = new Retrofit.Builder().baseUrl(ApiConfig.BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build()
Himanshu Virmani
sumber
1
Tautan bagus! Akan diperiksa nanti.
rmuller
1
Dalam dokumentasinya masih ada RestAdapter dan tidak ada contoh cara membuat request paling sederhana dengan Retrofit 2.0. Saya mendapatkan kesalahan Tidak dapat menyelesaikan RxJavaCallAdapterFactory. Menghabiskan beberapa jam untuk membuat permintaan http android paling sederhana. Tampaknya tidak sesederhana mereka beriklan. Mungkin mereka harus mendokumentasikan lebih banyak.
Vlado Pandžić
Gunakan Retrofit daripada RestAdapter jika Anda menggunakan Retrofit 2.0.
Himanshu Virmani
5
RxJavaCallAdapterFactory memerlukan artefak adapter-rxjava dari repo yang sama. compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
Damien Diehl
3
untuk retrofit2, pastikan Anda menggunakan dependensi yang benar com.squareup.retrofit2:adapter-rxjava:2.1.0dan com.squareup.retrofit2:converter-gson:2.1.0
George Papas
16

Jika Anda ingin menggunakan retrofit2dan Anda tidak ingin selalu kembali retrofit2.Call<T>, Anda harus membuatnya sendiri CallAdapter.Factoryyang mengembalikan tipe sederhana seperti yang Anda harapkan. Kode sederhananya dapat terlihat seperti ini:

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

Kemudian daftar sederhana SynchronousCallAdapterFactorydi Retrofit akan menyelesaikan masalah Anda.

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

Setelah itu Anda bisa mengembalikan tipe sederhana tanpa retrofit2.Call.

public interface SimpleService {
    @GET("/simple/{id}")
    Simple getSimple(@Path("id") String id);
}
Mateusz Korwel
sumber
Keren terima kasih. Tahukah Anda jika ada cara untuk membuat adaptor panggilan asinkron? jadi saya bisa memanggil layanan seperti itu: service.getSimple ("id", new Adapter () {onFail () {} onSuccess () {}} daripada menggunakan enqueue () dan callback?
markov00
"mengimplementasikan CallAdapter.Factory" Ini adalah kelas bukan antarmuka?
ozmank
@ Ozmank Dalam versi 2.0.0-beta3itu adalah antarmuka, sekarang dalam versi 2.1.0itu adalah kelas. Saya mengedit kode saya, agar lebih aktual. Terima kasih.
Mateusz Korwel
@MateuszKorwel Itu selalu mendapatkan lemparan RuntimeException baru (e)! Saya ingin mengembalikan String, bukan Simple.
Dr.jacky
3
Terima kasih memposting ini. Saya telah membuat pustaka seputar penanganan Simple getSimple()dan Response<Simple> getSimple()permintaan ini: github.com/jaredsburrows/retrofit2-synchronous-adapter .
Jared Burrows
13

Tambahkan dependensi berikut untuk retrofit 2

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

untuk GSON

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

untuk observasi

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

Dalam kasus Anda untuk XML, Anda harus menyertakan dependensi berikut

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

Perbarui panggilan layanan seperti di bawah ini

final Retrofit rest = new Retrofit.Builder()
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(SimpleXmlConverterFactory.create())
    .baseUrl(endpoint)
    .build();
SimpleService service = rest.create(SimpleService.class);
rahulrv
sumber
Ini sama dengan pengaturan saya.
rmuller
1
pastikan juga untuk menambahkan baris ini saat membuat adaptor: .addCallAdapterFactory (RxJavaCallAdapterFactory.create ())
Shayan_Aryan
Gagal menyelesaikan: com.squareup.retrofit: adapter-rxjava: 2.1.0
ozmank
1
@ozmank its retrofit2, saya telah memperbarui jawaban saya juga. Silakan mencobanya
rahulrv
5

IllegalArgumentException: Tidak dapat membuat adaptor panggilan untuk kelas java.lang.Object

Jawaban singkat: Saya telah menyelesaikannya dengan perubahan berikut

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"

Semoga berhasil

mehdi parsaei
sumber
4

dalam kasus saya menggunakan ini

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

dengan ini

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

memecahkan masalah ketika tidak ada yang berhasil

Hüseyin Bülbül
sumber
2

Jika Anda tidak menggunakan RxJava dengan benar, tidak masuk akal untuk menambahkan RxJava hanya untuk retrofit. 2.5.0memiliki dukungan untuk CompletableFuturebawaan yang dapat Anda gunakan tanpa menambahkan pustaka atau adaptor lain.

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

Pemakaian:

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

Hanya untuk membuat contoh Panggilan lebih jelas bagi orang-orang yang bermigrasi, tidak menggunakan Rx, atau yang menginginkan panggilan sinkron - Panggilan pada dasarnya menggantikan (membungkus) Respons, artinya:

Response<MyObject> createRecord(...);

menjadi

Call<MyObject> createRecord(...);

dan tidak

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

(yang masih membutuhkan adaptor)


Panggilan tersebut kemudian akan memungkinkan Anda untuk tetap menggunakan isSuccessfulkarena panggilan itu benar-benar mengembalikan Respons. Jadi Anda bisa melakukan sesuatu seperti:

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

Atau akses Jenis Anda (MyObject) seperti:

MyObject myObj = myApi.createRecord(...).execute().body();
Nick Cardoso
sumber
1
public interface SimpleService {

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

}

Komunikasi dengan jaringan dilakukan dengan utas terpisah sehingga Anda harus mengubah Sederhana Anda dengan itu.

public interface SimpleService {

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

}
Syed Waqas
sumber
1

Dalam kasus saya, saya menggunakan

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

dari pada

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

Anda harus menggunakan com.squareup.retrofit2:adapter-rxjava2:2.5.0saat menggunakanio.reactivex.rxjava2

svkaka
sumber
0

Anda dapat mengimplementasikan Callback, mendapatkan fungsi Simple dari 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
    }

}
Sithu
sumber
-2

Cara saya memperbaiki masalah ini adalah menambahkan ini ke file gradle aplikasi, jika konfigurasi tidak diatur akan ada konflik, mungkin ini akan diperbaiki dalam rilis pustaka yang stabil:

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

dan buat adaptor Anda dengan cara ini:

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

Semoga membantu!

Rensodarwin
sumber
1
Ini adalah konfigurasi yang persis sama dengan pertanyaan saya. Jadi, tidak menyelesaikan apa pun :)
rmuller